Description
给出一个数N,输出小于等于N的所有数,两两之间的最大公约数之和。
相当于计算这段程序(程序中的gcd(i,j)表示i与j的最大公约数):
由于结果很大,输出Mod 1000000007的结果。
G=0;
for(i=1;i
Solution
感觉有点不太对劲啊,为什么我推的柿子都和别人不太一样
先套路一波设一下
g(n)=∑i=1n∑j=1igcd(i,j)=∑i=1n∑j|ij∗φ(ij)=∑j=1nj∑i=1⌊ni⌋φ(i)
g
(
n
)
=
∑
i
=
1
n
∑
j
=
1
i
g
c
d
(
i
,
j
)
=
∑
i
=
1
n
∑
j
|
i
j
∗
φ
(
i
j
)
=
∑
j
=
1
n
j
∑
i
=
1
⌊
n
i
⌋
φ
(
i
)
那么答案有
ans=g(n)∗2−∑i=1ni
a
n
s
=
g
(
n
)
∗
2
−
∑
i
=
1
n
i
这里直接杜教筛筛出欧拉函数的前缀和,上分块搞
注意爆longlong的问题,直接开ull无济于事,气急败坏之下就一步步做
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const LL MOD=1000000007;
const int HASH=1299709;
const int N=21544346;
const LL ny2=500000004;
int prime[1362202],phi[N+1];
bool not_prime[N+1];
LL h[HASH+2],f[HASH+2];
bool found;
LL hash(LL x) {
found=false;
LL y=x%HASH;
while (h[y]&&h[y]!=x) {
y=y+1;
if (y>=HASH) y-=HASH;
}
if (h[y]==x) found=true;
h[y]=x;
return y;
}
void pre_work() {
phi[1]=1;
rep(i,2,N) {
if (!not_prime[i]) {
prime[++prime[0]]=i;
phi[i]=i-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=N;j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) {
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
phi[i]=(phi[i-1]+phi[i])%MOD;
}
}
inline LL get_phi(LL n) {
if (n<=N) return phi[n];
LL w=hash(n);
if (found) return f[w];
LL ret=0;
for (LL i=2,j;i<=n;i=j+1) {
j=n/(n/i);
LL tmp=(j-i+1)%MOD;
tmp=tmp*get_phi(n/i)%MOD;
ret=(ret-MOD+tmp)%MOD;
}
n=n%MOD;
LL tmp=(n+1)%MOD;
tmp=tmp*n%MOD;
tmp=tmp*ny2%MOD;
ret=(tmp-ret+MOD)%MOD;
f[w]=ret;
return ret;
}
void solve(LL n) {
// printf("%lld\n", get_phi(n));
LL ans=0;
for (LL i=1,j;i<=n;i=j+1) {
j=n/(n/i);
LL tmp=(i+j)%MOD;
tmp=tmp*(j-i+1)%MOD;
tmp=tmp*ny2%MOD;
tmp=tmp*get_phi(n/i)%MOD;
ans=(ans-MOD+tmp)%MOD;
}
n=n%MOD;
ans=ans*2%MOD;
LL tmp=(n+1)%MOD;
tmp=tmp*n%MOD;
tmp=tmp*ny2%MOD;
ans=(ans+MOD-tmp+MOD)%MOD;
printf("%lld\n", ans);
}
int main(void) {
pre_work();
LL n; scanf("%lld",&n);
solve(n);
return 0;
}