题目传送门
题意
求
(∑ni=1∑nj=1ijgcd(i,j)) mod p
(
∑
i
=
1
n
∑
j
=
1
n
i
j
g
c
d
(
i
,
j
)
)
m
o
d
p
,
n<=1010
n
<=
10
10
,
p
p
为质数。
题解
之前一直想着用函数搞,结果死都只能搞到
O(n)
O
(
n
)
。后来,我fa♂现了bzoj3518点组计数,这道题用了欧拉函数的一个性质
n=∑i|nφ(i)
n
=
∑
i
|
n
φ
(
i
)
,好像叫做欧拉反演?
于是新日暮里的大门打开了。
于是开始推式子。
∑ni=1∑nj=1ijgcd(i,j)
∑
i
=
1
n
∑
j
=
1
n
i
j
g
c
d
(
i
,
j
)
=∑ni=1∑nj=1ij∑k|gcd(i,j)φ(k)
=
∑
i
=
1
n
∑
j
=
1
n
i
j
∑
k
|
g
c
d
(
i
,
j
)
φ
(
k
)
=∑nk=1k2φ(k)∑⌊nk⌋i=1∑⌊nk⌋j=1ij
=
∑
k
=
1
n
k
2
φ
(
k
)
∑
i
=
1
⌊
n
k
⌋
∑
j
=
1
⌊
n
k
⌋
i
j
我们令
S(n)=∑ni=1i=n(n+1)2
S
(
n
)
=
∑
i
=
1
n
i
=
n
(
n
+
1
)
2
则原式
=∑nk=1k2φ(k)S(⌊nk⌋)2
=
∑
k
=
1
n
k
2
φ
(
k
)
S
(
⌊
n
k
⌋
)
2
于是我们只需要能够快速求出
∑nk=1k2φ(k)
∑
k
=
1
n
k
2
φ
(
k
)
就可以分块求和计算了。
这不就是杜教筛干的活吗?
∑ni=1i2∑j|iφ(j)
∑
i
=
1
n
i
2
∑
j
|
i
φ
(
j
)
=∑ni=1i3
=
∑
i
=
1
n
i
3
=S(n)2
=
S
(
n
)
2
=∑nj=1∑⌊nj⌋i=1i2j2φ(j)
=
∑
j
=
1
n
∑
i
=
1
⌊
n
j
⌋
i
2
j
2
φ
(
j
)
因为
i≤nj
i
≤
n
j
所以
j≤ni
j
≤
n
i
因此原式
=∑ni=1i2∑⌊ni⌋j=1j2φ(j)
=
∑
i
=
1
n
i
2
∑
j
=
1
⌊
n
i
⌋
j
2
φ
(
j
)
把
i=1
i
=
1
带人
得
∑ni=1i2φ(i)=S(n)2−∑ni=2i2∑⌊ni⌋j=1j2φ(j)
∑
i
=
1
n
i
2
φ
(
i
)
=
S
(
n
)
2
−
∑
i
=
2
n
i
2
∑
j
=
1
⌊
n
i
⌋
j
2
φ
(
j
)
然后就递归计算即可。细节详见代码。
这道题就解决了!
代码
#include<cstdio>
typedef long long ll;
const ll N=5000005,M=1000017;
ll mod,n,ans,p[N],phi[N];
bool vis[N];
struct hashmap{
int cnt,head[M],nxt[M*10];
ll to[M*10],v[M*10];
bool count(ll f){
ll x=f%M;
for(int i=head[x];i;i=nxt[i]){
if(to[i]==f){
return true;
}
}
return false;
}
ll get(ll f){
ll x=f%M;
for(int i=head[x];i;i=nxt[i]){
if(to[i]==f){
return v[i];
}
}
return 0;
}
void set(ll f,ll val){
ll x=f%M;
to[++cnt]=f;
nxt[cnt]=head[x];
v[cnt]=val;
head[x]=cnt;
}
}mp;
inline ll sqr(ll x){
x%=mod;
return x*x%mod;
}
ll calc(ll x){
if(!x){
return 0;
}
x%=mod;
return sqr((1+x)*x/2)%mod;
}
ll get(ll n){
n%=mod;
if(n%3!=1){
return n*(n+1)/6%mod*(2*n+1)%mod;
}else if(n&1){
return (n+1)*(2*n+1)/6%mod*n%mod;
}else{
return n*(2*n+1)/6%mod*(n+1)%mod;
}
}
ll solve(ll n){
if(n<=5000000){
return phi[n];
}
if(mp.count(n)){
return mp.get(n);
}
ll res=calc(n);
for(ll i=2,last;i<=n;i=last+1){
last=n/(n/i);
res-=solve(n/i)*(get(last)-get(i-1))%mod;
res%=mod;
}
mp.set(n,res);
return res;
}
int main(){
scanf("%lld%lld",&mod,&n);
phi[1]=1;
for(int i=2;i<=5000000;i++){
if(!vis[i]){
p[++p[0]]=i;
phi[i]=i-1;
}
for(int j=1;j<=p[0]&&i*p[j]<=5000000;j++){
vis[i*p[j]]=true;
if(i%p[j]){
phi[i*p[j]]=phi[i]*(p[j]-1);
}else{
phi[i*p[j]]=phi[i]*p[j];
}
}
}
for(int i=1;i<=5000000;i++){
phi[i]=phi[i]*i%mod*i%mod;
phi[i]+=phi[i-1];
phi[i]%=mod;
}
for(ll i=1,last;i<=n;i=last+1){
last=n/(n/i);
ans+=(solve(last)-solve(i-1))*calc(n/i)%mod;
ans%=mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}
最后附上蒟蒻博主龙hou飞yan凤wu舞chi的公式草稿一张2333 鼠标写字真是太方便啦