题目大意
求 ∑ni=1∑d|igcd(d,id)
数据范围n<= 1011
莫比乌斯反演
题目相当于求
∑ni=1∑[ni]j=1gcd(i,j)
很像一道经典莫比乌斯反演题了。
设f(k)表示gcd(i,j)=k的对数,设g(k)表示k|gcd(i,j)的对数。
于是有
f(k)= ∑[nk]i=1μ(i)∗g(i∗k)
所以问题变成了求
∑n√k=1k∗∑[nk]i=1μ(i)∗g(i∗k)
我们发现g并不能O(1)得出,然后就连O(n)都做不到了,但是别急。
我们设T=i*k,然后交换主体,于是原式就等于
∑nT=1g(T)∗∑i|Tμ(i)∗Ti
显然当T>
n√
时g的值都为0所以T只需要枚举到
n√
。
我们发现后面的部分只跟T有关系,所以可以预处理出来,用a(T)表示
然后,我们得到了一个十分优美的式子
∑n√T=1g(T)∗a(T)
现在问题的关键变成怎样求g了。
g(k)= ∑[nk]i=1[ni∗k2]
所以我们可以分块O(
nk−−√
)计算g(k),于是总复杂度就是O(
∑n√k=1nk−−√
)=O(
n34
)(由于本人太弱,也不知道这个怎么算出来的)。
同时我们发现
[ni∗k2]
很快就会返回0。
于是当它为0时,就可以退出计算当前的g(k),这样就可以跑得挺快了。
另外
该题有很多解法,本人比较蠢,只会莫比乌斯反演,出题人的方法简洁高效,
然而我没听懂(我太弱鸡了)。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=316227;
ll a[maxn+5],mu[maxn+5],c[maxn+5];
bool bz[maxn+5];
int main()
{
ll n,m,ans=0;
scanf("%lld",&n);
m=sqrt(n);
mu[1]=1;
for (int i=2;i<=m;i++){
if (!bz[i]){
mu[i]=-1;
c[++c[0]]=i;
}
for (int j=1;j<=c[0];j++){
if (i*c[j]>m) break;
bz[i*c[j]]=1;
if (i%c[j]==0){
mu[i*c[j]]=0;
break;
}
mu[i*c[j]]=-mu[i];
}
}
for (int i=1;i<=m;i++) if (mu[i]!=0){
for (int j=1;i*j<=m;j++) a[i*j]+=mu[i]*j;
}
for (int k=1;k<=m;k++){
ll m1=n/k,l=1,r=0,x=0,m2=m1/k,s=0;
while (l<=m1){
x=m2/l;
if (x==0) break;
r=m2/x;
s+=(r-l+1)*x;
l=r+1;
}
ans+=s*a[k];
}
printf("%lld\n",ans);
}