基本数论(从整除入门到欧拉弃坑到中国剩余BSGS跑路)

来源:互联网 发布:专业英语翻译软件 编辑:程序博客网 时间:2024/06/05 04:40

毕业了一点都不开心。
ps:本来就是自己看看所以随便写点什么都可以

整除

首先,我们需要一些看起来很有用然而除了装逼没有任何价值的定理:

1.如果abbc,那么ac(等同放p)
2.ab等价于对任意整数xy,有a(bx+cy)
3.设m0,那么ab等价于(ma)(mb)(此条等同于放p)
4.设整数x和y满足下式:ax+by=1,且anbn,那么(ab)n
5.若b=qc,那么db的充要条件是dc

剩下的空着吧

同余

首先,我们要知道:如果a与b关于模m同余,记为:ab(mod m)
那么,对于整数a,b,c和自然数m,n

1.自反性:aa(mod m)
2.对称性:若ab(mod m),则ba(mod m)
3.传递性:若ab(mod m)bc(mod m),则ac(mod m)
4.同加性:若ab(mod m),则a+cb+c(mod m)
5.同乘性:若ab(mod m),则acbc(mod m)
ab(mod m)cd(mod m),则acbd(mod m)
6.同幂性:若ab(mod m),则anbn(mod m)
7.推论1:ab mod k=(a mod k)(b mod k)mod k
8.推论2:若a mod p=xa mod q=x,p、q互质,则a mod pq=x

辗转相除法

int gcd(int a,int b) {return b?gcd(b,a%b):a;}

二进制超级卡常版辗转相除法

template<class T> T gcd(T a,T b) {    if(!a|!b) return a|b;    int i,j;    for(i=0;!(x&1);++i) x>>=1;    for(j=0;!(y&1);++j) y>>=1;    for(i=j<i?j:i;;) {        if(x<y) x^=y,y^=x,x^=y;        if(!(x-=y)) return y<<i;        for(;!(x&1);) x>>=1;    }}

脱非扩欧

相信大家都会不定方程的分解因数法,那么在这里就可以简单带过:
求一对x,y,使得ax+by=gcd
根据某些数论知识,这个方程显然有解
另某一组特殊解为x0,y0,那么就可以得到通项公式:
x=x0+b/gcdt
y=y0a/gcdt
由于gcd的结束条件是a=gcd,b=0,此时有解x1=1,y1=0,根据gcd(a,b)==gcd(b,a%b),我们可以直接推导:

gcd=bx1+(a(a/b)b)y1        =bx1+ay1(a/b)by1   =ay1+b(x1a/by1)

那么:x1=y1,y1=x1-a/b*y1

template <class T> T e_gcd(T a,T b) {    if(!b) {x=1,y=0;return a;}    int temp=x;    gcd=e_gcd(b,a%b);    x=y,y=temp-a/b*y;    return gcd;}

然后,不定方程ax+by=c的特殊解x1,y1,就是不定方程ax+by=gcd的特殊解x0,y0乘上c/gcd,然后按照通项公式即可推导。
x=x1+b/gcdt
y=y1a/gcdt
至于求逆元,我们可以当成不定方程ax+by=1
ZOJ3609,代码长这样:

#include<cstring>#include<cstdio>using namespace std;typedef long long LL;template <class T> void read(T &x) {    x=0;LL f=1;char ch=getchar();    for(;ch<'0'||ch>'9';) {if(ch=='-') f=-1;ch=getchar();}    for(;ch>='0'&&ch<='9';) x=x*10+ch-'0',ch=getchar();x*=f;}template <class T> void write(T x) {    LL num=0;char ch[20];    if(x<0) putchar('-'),x=-x;    do ch[++num]=x%10+'0',x/=10; while(x);    for(;num;) putchar(ch[num--]);putchar('\n');}LL e_gcd(LL a,LL b,LL &x,LL &y) {    if(!b) {x=1,y=0;return a;}    LL temp,gcd;    gcd=e_gcd(b,a%b,x,y),temp=x;    x=y,y=temp-a/b*y;    return gcd;}LL cal(LL a,LL b,LL c) {    LL ans,gcd,x,y;    gcd=e_gcd(a,b,x,y);    if(c%gcd!=0) return -1;    x*=c/gcd,b/=gcd;    if(b<0) b=-b;    ans=x%b;    if(ans<=0) ans+=b;    return ans;}int main() {    LL a,b,ans,test;    for(read(test);test--;) {        read(a),read(b),ans=cal(a,b,1);        if(ans!=-1) write(ans);else puts("Not Exist");    }    return 0;}

POJ1061 裸裸的
ZOJ3593 画出两条直线yy一下
HDU1576 求逆元裸题,然而可以暴力
HDU2669 裸题(吐嘈一下比赛名字)
(天哪一个上午+两个小时就干了这么一点点东西简直了……)

求解线性同余方程

求解axb(mod n)相当于求解ax+ny=b
参照上方不定方程求解。
由于只需要一个解,所以这里可以简单一点。
设间隔为dx,简单推导:
axb(mod n)
a(x+dx)b(mod n)
相减:
adx0(mod n)(可以这么写吧?)
好咯,那么adx就是a和n的公倍数了。为了dx最小,adx就是最小公倍数。
所以dx=n/gcd(a,n)
瞎搞瞎搞。

中国剩余定理

孙子算经:
之前一直以为很高深啊很高深,老祖宗们真是太聪明了……
不过发现原来就是套,不过祖宗们套的方法高深一些,其实与小学生们的解法是有一定相似之处的…..
孙子问题解法的本质是从5和7的公倍数中找一个除以3余2的数n1,从3和7的公倍数中找一个除以5余3的数n2,从3和5的公倍数中找一个除以7余2的数n3,再将三个数相加得到解。在求n1,n2,n3时又用了一个小技巧(ak) mod b=k(a mod b),以n1为例,并非从5和7的公倍数中直接找一个除以3余2的数,而是先找一个除以3余1的数,再乘以2。
最后,我们还要清楚一点,n1+n2+n3只是问题的一个解,并不是最小的解。如何得到最小解?因为显然有(a±kb) mod b=a mod b,所以(n1+n2+n3) mod 105就是最终的最小解。

POJ1006 裸裸裸
中国剩余定理:

自然数m1m2......mk两两互素,求最小正整数解:

xc1(mod m1)xc2(mod m2)xc3(mod m3)...xck(mod mk)

那么就有:
M=m1m2m3......mkinv(Mmi,mi)表示逆元
x=(ki=1(ciMmiinv(Mmi,mi)))mod M

证明通俗易懂就不说了还不是懒
codevs3990

#include<cstring>#include<cstdio>using namespace std;typedef long long LL;LL m[15],c[15],ans,Min,M=1;LL exgcd(LL a,LL b,LL &x,LL &y) {    if(!b) x=1,y=0;    else exgcd(b,a%b,y,x),y-=a/b*x;}int main() {    LL a,b,l,r,x,y,N=0;int i,n;    for(scanf("%d%lld%lld",&n,&l,&r),i=1;i<=n;i++)        scanf("%lld%lld",m+i,c+i),M*=m[i];    for(i=1;i<=n;i++) {        a=M/m[i],b=m[i],exgcd(a,b,x,y);        x=(x%b+b)%b;        if(!x) x+=b;        N+=c[i]*a*x;    }    N%=M;    if(!N) N+=M;    if(r>=N) ans=(r-N)/M+1;    if(l>=N) ans=ans-((l-N)/M+1);    if((l-N)%M==0) ++ans;    if(ans) {        if(l<=N) Min=N;        else Min=N+((l-N)/M+1)*M;    }    printf("%lld\n%lld\n",ans,Min);    return 0;}

扩展

然后还有一个扩展(不互素怎么办捏?):

puts("先骗偏访问量然后过来填坑");

素数

素数相关定理

1.唯一分解定理(p话)
2.威尔逊定理

若p为素数,则(p1)!1(mod p)
若对某一正整数p,有(p1)!1(mod p)

3.费马定理

若p为素数,a为正整数,且a和p互质,则:ap11(mod p)
小定理:

apa(mod p)

欧拉函数

φ(n):定义为小于n且与n互素的数的个数。
为积性函数,即当(m,n)=1时,φ(mn)=φ(m)φ(n),但并非完全积性函数。
1.n求法:

void euler(int n) {    int m=(int)sqrt(n+0.5);    int ans=n;    for(int i=2;i<=m;i++)        if(!(n%i)) for(ans=ans/i*(i-1);!(n%i);) n/=i;    if(n>1) ans=ans/n*(n-1);}

2.O(n)线性筛
1.phi(p)==p1 因为素数p除了1以外的因子只有p,所以与 p 互素的个数是 p - 1个
2. phi(pk)==pkpk1==(p1)pk1

证明:
n==pk,小于 n 的正整数共有 pk1个,其中与 p 不互素的个数共pk11个,它们是 1p,2p,3p...(pk11)p
所以phi(pk)==(pk1)(pk11)==pkpk1==(p1)pk1

3.如果pi, 那么 phi(ip)==pphi(i)
4.如果pi, 那么phi(ip)==phi(i)(p1)

int prime[MAXN],check[MAXL],phi[MAXL];int main() {    int i,j,tot=0;    phi[1]=1;    memset(check,0,sizeof(check));    for(i=2;i<MAXL;++i) {        if(!check[i]) prime[tot++]=i,phi[i]=i-1;        for(j=0;j<tot&&i*prime[j]<=MAXL;++j) {            check[i*prime[j]]=1;            if(i%prime[j]==0) {                phi[i*prime[j]]=phi[i]*prime[j];                break;            } else phi[i*prime[j]]=phi[i]*(prime[j]-1);        }    }}

一道必刷题:poj2480
f(n)=Ni=1gcd(i,N)
公式为:f(n)=x|Nxφ(N/x)
gcd(i,N)显然是个积性函数
由于积性函数的和必然是积性函数,所以此题可以利用积性做

Baby Steps Giant Steps及其扩展

又名拔山盖世算法、大步小步算法、北上广深算法、百事公司算法。
用来解决这么一个式子
AxB  (mod C)C为素数)

先令x=imj,其中m=C
这样原式就变为AimjB(mod C)
再变为Aj×BAmi(mod C)
枚举j(范围0-m)(显然),将Aj×B存入hash表
枚举i(范围1-m),从hash表中寻找第一个满足Aj×BAmi(modC)
此时x=imj即为所求。
在网上大多用的是x=im+j,也可以做,只是会牵扯的求逆元,所以比较麻烦。使x=imj就可以避免这个问题了。
有一个公式 ak mod p1ak(mod p) 这个公式的推导需要用到费马小定理
k mod p1可以看做 km(p1) ,原式可化成 ak/(ap1)mak(mod p)
根据费马小定理ap11(mod p)其中p为质数 ,a,p 互质,可得ak/1mak(mod p)akak(mod p) 得证。

代码什么的,只要会打快速幂,剩下的就是暴力枚举了。
模板题poj2417(其实大佬们敲不敲随便的啦)

扩展:与中国剩余定理一样的方式

如果C不是素数怎么办(┙>∧<)┙へ┻┻?
凉拌,炒鸡蛋

d=gcd(A,C),如果dB显然只有x=0,B=1这一种解
如果dB:
首先,如果C与A互质,那么直接BSGS
如果不互质怎么办?
那么Ax1AdBd (mod Cd)
如果现在互素了,开始求x-1
如果还不互素,就一直求到d2......dk
现在ACki=1di互素

AxkAkki=1diBki=1di (mod Cki=1di)

如果ki=1diB,只有x=0这唯一解
如果ki=1diB,暴力枚举x[0,k),找到了就找到了,对于x>k:
AxkBAk(mod Cki=1di)

x=xkB=BAkC=Cki=1di
AxB (mod C)

然后BSGS来一遍求出x,正解x就是x+k

卧曹怎么看着这么像暴力啊
没错好像BSGS就是暴力啊
poj3243

原创粉丝点击