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}
lnnn的时间,因此,最坏最坏的情况的时间复杂度为
O
(
n
+
m
n
log
n
ln
n
)
O(\sqrt n +m\cfrac{\sqrt n\log n}{\ln{\sqrt n}} )
O(n+mlnnnlogn)而最坏的情况显然不能发生,所以真正的时间复杂度要远远小于
O
(
n
+
m
n
log
n
ln
n
)
O(\sqrt n +m\cfrac{\sqrt n\log n}{\ln{\sqrt n}} )
O(n+mlnnnlogn)