SQH法的由来
今日刷题时,遇到了一个本蒟蒻想一下午也想不出来AC方法的题:P3601 签到题
对 于 100 % 的 数 据 , 1 ≤ l ≤ r ≤ 1 0 12 r − l ≤ 1 0 6 对于100\%的数据,1≤l≤r≤10^{12} r−l≤10^6 对于100%的数据,1≤l≤r≤1012r−l≤106发现这数据范围不能(反正我是不会)用正常线性筛或者用通解公式来求,前者没法开这么大的数组,后者的时间复杂度为 O ( ( r − l ) r ) O((r-l) \sqrt{r}) O((r−l)r ),于是乎,我就苦思冥想,用递推法歪打正着的发明了一种新算法来求欧拉函数
应用范围:需要求欧拉函数的特别大,大于数组能开的范围
原理:
1.一个大数 n n n只可能有一个质因子大于 n \sqrt{n} n
2.当 n n n为质数, ϕ ( p ) = p − 1 \phi (p)=p-1 ϕ(p)=p−1
3.当 p p p为质数, p ∣ n p|n p∣n, i % p = = 0 i\%p==0 i%p==0时, ϕ ( p ∗ i ) = = p ∗ ϕ ( i ) \phi (p*i)==p*\phi (i) ϕ(p∗i)==p∗ϕ(i)
4.当 p p p为质数, p ∣ n p|n p∣n, i % p ! = 0 i\%p!=0 i%p!=0时, ϕ ( p ∗ i ) = = ( p − 1 ) ∗ ϕ ( i ) \phi (p*i)==(p-1)*\phi(i) ϕ(p∗i)==(p−1)∗ϕ(i)
提前求出1~ n \sqrt n n 的质数
不断除以质因子,用递归把n化为质数,然后边界回归n-1,或者提前求出1~ n \sqrt n n 的欧拉函数,当n小于 n \sqrt n n 是,回归即可
Code:
#pragma comment(linker, "/STACK:102400000,102400000")
/*************************
一定要加上面那一行!防止爆栈
*************************/
//f[n]=0说明n为质数,f[n]=1为合数
//p存储质数
void GetPrime(){
phi[1]=1;
for(int i=2;i<=1000001;++i){
if(!f[i]){
p[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt&&p[j]*i<=1000001;++j){
f[p[j]*i]=1;
if(i%p[j]) phi[p[j]*i]=(p[j]-1)*phi[i];
else{
phi[p[j]*i]=p[j]*phi[i];
break;
}
}
}
}
ull Near(ull num){//判断大于根号p的质数在p中的下标
for(ull i=num;;++i){
if(f[i]==0) return i;
}
}
ull SearchPhi(ull num){
if(num==Memory) return num-1;//至关重要!设置Memory变量来判断n是否已经没有质因子
Memory=num;
if(num<=1000000) return phi[num];
for(ull i=1;i<=Near(sqrt(num));++i){
if(num%p[i]==0){
ull j=num/p[i];
if(j%p[i]==0) return p[i]*SearchPhi(j);
else return (p[i]-1)*SearchPhi(j);
}
}
}
时间复杂度:
设数的大小为n,要求m个数
求质数产生的 O ( n ) O(\sqrt n) O(n )的时间不计,最坏的情况下质因子全为2,有 log n \log n logn个质因子,而根据质数分布的稀疏度,大概枚举完每个质因子需要 n ln n \sqrt n\over \ln{\sqrt n} lnn n 的时间,因此,最坏最坏的情况的时间复杂度为 O ( n + m n log n ln n ) O(\sqrt n +m\cfrac{\sqrt n\log n}{\ln{\sqrt n}} ) O(n +mlnn n logn)而最坏的情况显然不能发生,所以真正的时间复杂度要远远小于 O ( n + m n log n ln n ) O(\sqrt n +m\cfrac{\sqrt n\log n}{\ln{\sqrt n}} ) O(n +mlnn n logn)