【GDSOI 2016】第一题 互补约数

来源:互联网 发布:intel edison linux 编辑:程序博客网 时间:2024/04/30 11:31

Description

简化题意:求

i=1nj|igcd(j,ij)

Input

输入包含一行,一个正整数 n。

Output

输出只有一行, F(n)。

Sample Input

Sample Input1:
10

Sample Input2:
1000

Sample Output

Sample Output1:
32

Sample Output2:
12776

Data Constraint

n<=1011

Solution

两个sigma外加gcd,直接考虑反演

i=1nj|igcd(j,ij)

这里是枚举i和i的约数,我们转换为枚举i和i的倍数
i=1nj=1nigcd(i,j)

套路反演

f[d]=i=1nj=1nigcd(i,j)=d

g[d]=i=1ndf[id]

反演得

f[d]=i=1ndg[id]μ(i)

ans=d=1ndi=1ndg[id]μ(i)


T=id,我们发现gcd(i,j)的值不可能大于n,所以

ans=T=1ng[T]i|TTiμ(i)

nT=1i|TTiμ(i)预处理,设为a(i)
为了方便预处理,设ij=T变形为
a(i)=i=1ni=1ndjμ(i)

我们知道,如果gcd(x,y)为T的倍数,那么x,y也是T的倍数
那么x=xT,y=yT
因为xy<=n
所以x
枚举xTyT<=n
所以

g[T]=x=1nT2nT2x


于是,你会惊奇的发现,答案变成了

ans=T=1ng[T]a(t)

很简单对吧
但还是过不了
求g[t]的时候用分块优化一下,而t本身不能分块(为什么?)

时间复杂度O(n34)

Code

#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define ll long long#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 401000using namespace std;ll n,mu[N],a[N],bz[N],s[N];int main(){    freopen("gcd.in","r",stdin);freopen("gcd.out","w",stdout);    scanf("%lld",&n);    ll m=sqrt(n);    mu[1]=1;    fo(i,2,m)    {        if(!bz[i]) s[++s[0]]=i,mu[i]=-1;        fo(j,1,s[0])        {            int k=i*s[j];            if(k>m) break;            bz[k]=1;            if(i%s[j]==0) {mu[k]=0;break;}            mu[k]=-mu[i];        }    }    fo(i,1,m) fo(j,1,m/i) a[i*j]+=j*mu[i];    ll ans=0;    for(ll t=1;t<=m;t++)    {        ll jy=0,q=n/(t*t);        for(ll x=1,y=1;x<=q;y=x+1,x=min((q/(q/y)),q))         {            jy+=(x-y+1)*(q/x);            if(x>=q) break;        }        ans+=(jy*a[t]);    }    printf("%lld",ans);}
1 0
原创粉丝点击