文章目录
题目链接:
http://www.51nod.com/Challenge/Problem.html#!#problemId=1237
我们想要求所有的
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)的和,换一个思路就是看
g
c
d
(
i
,
j
)
=
d
gcd(i,j)=d
gcd(i,j)=d对答案的贡献,(感觉好多题都是换一种思路来看对答案的贡献的),那如果能够快速求出
g
c
d
(
i
,
j
)
=
d
gcd(i,j)=d
gcd(i,j)=d的有几个就好了
假如说
g
c
d
(
i
,
j
)
=
d
gcd(i,j)=d
gcd(i,j)=d的有
c
n
t
(
d
)
cnt(d)
cnt(d)个,那么
a
n
s
=
∑
d
=
1
n
d
⋅
c
n
t
(
d
)
ans=\sum_{d=1}^nd \cdot cnt(d)
ans=d=1∑nd⋅cnt(d)
那么现在结论就是:
c
n
t
(
d
)
=
2
⋅
S
(
n
d
)
−
1
cnt(d)=2\cdot S(\frac{n}{d})-1
cnt(d)=2⋅S(dn)−1
其中S(n)表示欧拉函数前缀和:
S
(
n
)
=
∑
i
=
1
n
φ
(
i
)
S(n)=\sum_{i=1}^n\varphi(i)
S(n)=i=1∑nφ(i)
那么为什么喃?
先来看这个:
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
1
]
=
2
∑
i
=
1
n
∑
j
=
1
i
−
1
[
g
c
d
(
i
,
j
)
=
=
1
]
+
1
\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i-1}[gcd(i,j)==1]+1
i=1∑nj=1∑n[gcd(i,j)==1]=2i=1∑nj=1∑i−1[gcd(i,j)==1]+1
这个还是很好理解的,因为
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j)和
g
c
d
(
j
,
i
)
gcd(j,i)
gcd(j,i)是对称的,主对角线分成两半就行
加1是因为
g
c
d
(
1
,
1
)
=
1
gcd(1,1)=1
gcd(1,1)=1
然后再修改一哈,把
g
c
d
(
1
,
1
)
gcd(1,1)
gcd(1,1)加进去,
j
j
j从
1
1
1到
i
i
i而不是
i
−
1
i-1
i−1了
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
1
]
=
2
∑
i
=
1
n
∑
j
=
1
i
[
g
c
d
(
i
,
j
)
=
=
1
]
−
1
\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\sum_{j=1}^{i}[gcd(i,j)==1]-1
i=1∑nj=1∑n[gcd(i,j)==1]=2i=1∑nj=1∑i[gcd(i,j)==1]−1
有了这个之后,来看里面那层求和,其实这层求和就是欧拉函数的定义,所以
∑
j
=
1
i
[
g
c
d
(
i
,
j
)
=
=
1
]
=
φ
(
i
)
\sum_{j=1}^{i}[gcd(i,j)==1]=\varphi(i)
j=1∑i[gcd(i,j)==1]=φ(i)
所以:
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
1
]
=
2
∑
i
=
1
n
φ
(
i
)
−
1
\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2\sum_{i=1}^n\varphi(i)-1
i=1∑nj=1∑n[gcd(i,j)==1]=2i=1∑nφ(i)−1
所以:
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
1
]
=
2
S
(
n
)
−
1
\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==1]=2S(n)-1
i=1∑nj=1∑n[gcd(i,j)==1]=2S(n)−1
那现在要找 g c d ( i , j ) = d gcd(i,j)=d gcd(i,j)=d的个数就是求 ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = d ] \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d] ∑i=1n∑j=1n[gcd(i,j)==d]
因为如果:
g
c
d
(
i
,
j
)
=
=
d
gcd(i,j)==d
gcd(i,j)==d,那么
g
c
d
(
i
d
,
j
d
)
=
1
gcd(\frac{i}{d},\frac{j}{d})=1
gcd(di,dj)=1
所以就有:
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
d
]
=
∑
i
=
1
n
d
∑
j
=
1
n
d
[
g
c
d
(
i
,
j
)
=
=
1
]
=
2
⋅
S
(
n
d
)
−
1
\sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)==d]=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}[gcd(i,j)==1]=2\cdot S(\frac{n}{d})-1
i=1∑nj=1∑n[gcd(i,j)==d]=i=1∑dnj=1∑dn[gcd(i,j)==1]=2⋅S(dn)−1
好有了公式 a n s = ∑ d = 1 n d ⋅ ( 2 ⋅ S ( n d ) − 1 ) ans=\sum_{d=1}^nd \cdot (2\cdot S(\frac{n}{d})-1) ans=d=1∑nd⋅(2⋅S(dn)−1)之后,貌似 d d d还是要从1遍历到n,还是至少都是线性复杂度啊
这里就用到了整除分块了, n d \frac{n}{d} dn不变的这段区间,虽然 d d d会变,但是是公差为1的等差数列,所以这一段的d也能一口气计算,所以整个复杂度就低了
#include"bits/stdc++.h"
using namespace std;
const int maxn=1e6+5;
typedef long long LL;
const LL MOD=1e9+7;
const LL inv2=5e8+4;
bool vis[maxn];
LL phi[maxn],Sphi[maxn];//欧拉函数,欧拉函数前缀和
vector<int>prime;
map<LL,LL>Mp;
void PHI(int n)
{
memset(vis,1,sizeof(vis));
phi[1]=1;
Sphi[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i])
{
prime.push_back(i);
phi[i]=i-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=0;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
Sphi[i]=(Sphi[i-1]+phi[i])%MOD;
}
}
LL S(LL n)//计算欧拉函数前缀和
{
if(n<=maxn-5)return Sphi[n];
if(Mp[n])return Mp[n];
LL res=(n%MOD)*((n+1)%MOD)%MOD*inv2%MOD;
for(LL i=2,j;i<=n;i=j+1)
{
j=n/(n/i);
res-=(LL)(j-i+1)%MOD*S(n/i)%MOD;
res%=MOD;
}
res=(res+MOD)%MOD;
return Mp[n]=res;
}
LL calc(LL i,LL j)//计算从i到j的公差为1的等差数列的和
{
LL res=(i+j)%MOD*(j-i+1LL)%MOD*inv2%MOD;
return res%MOD;
}
int main()
{
PHI(maxn-5);
LL N;
while(cin>>N)
{
LL res=0;
for(LL d=1,j;d<=N;d=j+1)
{
j=N/(N/d);
res+=calc(d,j)*(2LL*S(N/d)-1)%MOD;
res%=MOD;
}
res=(res+MOD)%MOD;
cout<<res<<endl;
}
}