标签:思路 扩展欧几里得 time 证明 最好 自然数 int 循环 ...
任何一个大于1的自然数都可以唯一分解成有限个素数的乘积
$N=p_1^{a_1}\times p_2^{a_2}\times...\times p_n^{a_n} | p_1<p_2<...<p_n ,a_i\in Z$
上式中$p_i$为素数
埃式筛法
就是拿一个已经筛选出的素数去排掉其他的数,比如2是素数,就用他筛掉2*2,2*3,2*4……,3是素数,就用他筛掉3*2,3*3,3*4……,把每个素数的倍数都筛掉,不过我们发现,3*2事实上已经在2的时候筛掉过了,即比这个数小的倍数都已经被筛过了,所以我们只要从这个数的平方开始筛就可以了。这个筛法最容易理解也最好写,一定要会写!经过这个筛法,能打素数表,能判定一个数是不是素数。复杂度O(nloglogn)。也可以用这种筛法筛出因子。
1 const int N=1e6+7; 2 bool isprime[N]; 3 int prime[N];//储存素数,打表 4 void db() 5 { 6 memset(isprime,1,sizeof(isprime));//这步事实上不太好,不过因为是bool类型所以不影响结果 7 isprime[0]=isprime[1]=0; 8 int cntp=0; 9 for(long long i=2;i<N;i++) 10 { 11 if(isprime[i]) 12 { 13 prime[cntp++]=i; 14 for(long long j=i*i;j<N;j+=i)//i*i会爆int所以用ll 15 isprime[j]=0; 16 } 17 } 18 }
欧拉线性筛法
正常情况下就算是没有优化的埃式筛法应该也是够用的,但是我们发现埃式筛法还是不是最快的,比如12这个数会被2和3筛两次,诸如此类的重复会降低效率。所以我们给出欧拉线性筛法。这个筛法自然比较难理解一点,其思路是这样的,从2开始,将这个素数和之前筛出的所有素数的乘积都判负,如果碰到i是一个合数,也用他去筛,但在筛的循环中碰到一个是他因子的素数就跳出(一个合数必定能表示成至少一个素数参与的分解式),达到避免重复筛的目的,然后一个循环后就筛出了素数。这个方法比较难理解,我也不是很能感受到这个方法的正确性,不过据说用反证法能进行证明。复杂度O(n)。
线性筛还能用在处理其他积性函数上。
1 const int N=1e6+7; 2 bool isprime[N]; 3 int prime[N]; 4 void db() 5 { 6 memset(isprime,1,sizeof(isprime)); 7 isprime[0]=isprime[1]=0; 8 int cntp=0; 9 for(int i=2;i<N;i++) 10 { 11 if(isprime[i]) 12 prime[cntp++]=i; 13 //这一部分是精髓所在也是难理解的部分,不管i是否是素数,都进循环去筛,i是合数时中途会跳出,达到减少重复筛的目的 14 for(int j=0;j<cntp&&i*prime[j]<N;j++) 15 { 16 isprime[i*prime[j]]=0; 17 if(!(i%prime[j])) 18 break; 19 } 20 } 21 }
求因子个数
前置定理:若p是一个素数,a是一个正整数,那么有$\tau(p^a)=a+1$。
定理:若正整数n有质因数分解$n=p_1^{a_1}\times p_2^{a_2}\times...\times p_k^{a_k}$,则$\tau(n)=(a_1+1)\times(a_2+1)\times...\times(a_k+1)$。
我们需要先打出一张素数表才能套用这个。
1 int factor_count(int n) 2 { 3 int ans=1; 4 int cnt; 5 int k=sqrt(n)+1; 6 for(int i=0;prime[i]<k;i++) 7 { 8 if(n%prime[i]==0) 9 { 10 cnt=0; 11 while(n%prime[i]==0) 12 { 13 cnt++; 14 n/=prime[i]; 15 } 16 ans*=(cnt+1); 17 } 18 } 19 if(n>1)//这一步是因为只筛到根号n,可能会留最后一个因子,此时需要再算上这个因子 20 ans*=2; 21 return ans; 22 }
素因子筛
利用埃式筛法处理,复杂度O(nloglogn)
1 const int maxn=1e5+7; 2 void db() 3 { 4 vector<int> d[maxn]; 5 for(int i=2;i<maxn;i++) 6 if(d[i].empty()) 7 { 8 for(int j=2*i;j<maxn;j+=i) 9 d[j].push_back(i); 10 } 11 }
辗转相除法
1 long long gcd(long long a,long long b) 2 { 3 return b?gcd(b,a%b):a; 4 }
扩展欧几里得
1 long long exgcd(long long a,long long b,long long &x,long long &y) 2 { 3 if(b==0) 4 { 5 x=1,y=0; 6 return a; 7 } 8 long long gcd=exgcd(b,a%b,y,x); 9 y-=a/b*x; 10 return gcd; 11 }
标签:思路 扩展欧几里得 time 证明 最好 自然数 int 循环 ...
原文地址:https://www.cnblogs.com/computer-luo/p/9459404.html