证明就算了吧,弱鸡太懒了,这也可能是为什么我是弱鸡的原因吧,决定记下结论会用就行了。
1、gcd():最大公约数
int gcd(a,b){//算法导论上指出a、b为非负整数,不用在意两者的大小关系 if b==0 return a; else return gcd( b,a % b) }//时间复杂度O(log max(a,b))
2、ex_gcd():扩展欧几里得算法
一定存在整数对(x,y)使得ax+by=gcd(a,b)。此处省略一万字数学证明
int extgcd(int a,int b,int x, int y){ int d =a; if(b!=0){ d = extgcd(b,a%b,y,x); y -= (a/b)*x; } else{ x=1; y=0; } return d;//返回的是gcd(a,b) ,时间内复杂度同上 }
3、素数:
1、对于单独判断一个数字是否为素数,只要从2~根号n之间找即可。
2、输出或统计n以内有多少素数时,采用埃式筛法
int prime[n];//n以内的素数打表 bool is_prime[n+1]; //返回n以内素数的个数 int sieve(int n){ int count=0; for(int i=0;i<=n;i++){ is_prime=true; } is_prime[0]=is_prime[1]=false; for(int i =2;i<=n;i++){ prime[count++] = i; //题外话,之前一直弄不清这个括号里写++的方式,是先令count=count+1后再把i赋值给prime[count] for (int j =2*i;j<=n;j+=i){ is_prime[j]=false; } } return count; } //时间复杂度O(nloglogn)可认为是线性的
3、求一个区间内的素数个数:
解释一下:因为b内的合数最小质因数一定不消耗国根号b。即先分别做好[2,根号b)的表和[a,b)的表,然后从小的表里筛得素数的同时,将他的倍数从大表中划去即可。
bool is_prime[1000010]; bool is_prime_small[1000010]; void segment_sieve(ll a, ll b) { for(int i=0; (ll)i*i<b; i++) is_prime_small[i] = true;//i是素数 for(int i=0; i<b-a; i++) is_prime[i] = true; //利用0~len代表a~b的数 for(int i=2; (ll)i*i<b; i++) { if(is_prime_small[i]) { for(int j=i+i; (ll)j*j<b; j+=i) is_prime_small[j] = false; //筛[2,√b) for(ll j=max(2LL, (a+i-1)/i)*i; j<b; j+=i) is_prime[j-a] = false; //筛[a,b) //j代表素数,j-a是将a~b变为0~b-a以便数组好存储 //2LL是2的长整形形式,与其比较意思是j最少是i的两倍 //((a+i-1)/i)*i得出的是(>=a && %i==0)离a最近的数,其实 //也可以写成a%i==0 ? a : (a/i+1)*i } } }