【题目太正式了我还怎么写ヾ|≧_≦|〃】
【很简要】
【参考文献:《算法导论》、白书、gty课件,LH课件】
[2016-08-13 ]
[2017-02-14 update (Valentine's Day就写这玩意?!]
1.基础
【除法定理】:对于任何整数a和正整数n,存在唯一整数q和r,满足0<=r<n且a=qn+r
WARN:C++中貌似不完全遵守这个东西,n认为是|n|,并且a为负时r可以为负
这是算法导论上的说法,有很多资料上并不遵守r是正整数
有用的式子: a%b=a-a/b*b
2.最大公约数
几条性质:gcd(a,b)=gcd(|a|,|b|)
gcd(a,0)=|a|
gcd(a,ka)=|a|
gcd(a,b)=gcd(b,a mod b) --->Euclid算法
gcd(na,nb)=n*gcd(a,b) ---->Stein算法
当k与b互为质数,gcd(ka,b)=gcd(a,b) ---->Stein算法
gcd(a,b)是a与b的线性组合集{ax+by: x,y belong Z}中最小的元素
【Euclid算法、扩展欧几里德算法】
int gcd(int a,int b){return b==0?a:gcd(b,a%b);} int lcm(int a,int b){return a/gcd(a,b)*b;} void exgcd(int a,int b,int &d,int &x,int &y){ if(!b) d=a,x=1,y=0; else exgcd(b,a%b,d,y,x),y-=(a/b)*x; }
扩展欧几里得算法:
求解 ax+by=gcd(a,b) 的解 (x0,y0) ,满足|x0|+|y0|最小,可能为负
过程:
边界 b==0时 ,d=a,x=1,y=0式子成立;
知道b*x1+a%b*y1=gcd(a,b)的(x1,y1) , 求ax+by=gcd(a,b)的(x,y)
用a%b=a-⌊a/b⌋*b来替换
x1*b + y1*a - y1*⌊a/b⌋*b = gcd(a,b)
y1*a + (x1-y1*⌊a/b⌋)*b = gcd(a,b)待定系数法
x=y1, y=x1-y1*⌊a/b⌋;
求解不定方程:
ax+by=c --> 如果 gcd(a,b) | c 同乘c/gcd(a,b)
直线上的整点
任意解: (x0+kb',y0-ka') , a'=a/gcd(a,b)=lcm(a,b)/b 想象坐标系,加上lcm(a,b)后还是在直线上的整点
【Stein算法】(更相减损术改)
原文:可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。
改进:利用上面那个性质
优势:不用除法 高精/位运算都很方便
int stein(int a,int b){
if(a==0||b==0) return a==0?b:a;
if(a&1==0&&b&1==0) return 2*stein(a>>1,b>>1);
if(a&1==0) return stein(a>>1,b);
if(b&1==0) return stein(a,b>>1);
return stein(min(a,b),abs(a-b));
}
3.唯一分解定理
一个比较好的分解质因子的写法,用了线性筛,保存lp[i]为数字i的最小质因子
唯一智障的缺点是必须先筛到n,所以n大时还是正常点筛sqrt(n)然后一个个质数试吧
bool notp[N]; int p[N],lp[N],lpnum[N]; void sieve(int n){ for(int i=2;i<=n;i++){ if(!notp[i]) p[++p[0]]=i,lp[i]=i,lpnum[i]=p[0]; for(int j=1;j<=p[0]&&i*p[j]<=n;j++){ notp[i*p[j]]=1;lp[i*p[j]]=p[j];lpnum[i*p[j]]=j; if(i%p[j]==0) break; } } } int e[N]; void fac(int x,int e[]){ while(x!=1){ int j=lpnum[x]; while(x%p[j]==0) x/=p[j],e[j]++; printf("e %d %d\n",p[j],e[j]); } } void invfac(int x,int e[]){ while(x!=1){ int j=lpnum[x]; while(x%p[j]==0) x/=p[j],e[j]--; } }