HDU 6134 莫比乌斯反演

来源:互联网 发布:linux mount cifs 编辑:程序博客网 时间:2024/05/22 08:06

题目链接


题意:
已知n<=1e6,求:

f(n)=i=1nj=1iij[gcd(i,j)==1]

思路:
首先套上莫比乌斯反演的经典转化:

d|nμ(d)=[n==1]

得:

f(n)=i=1nj=1iijd|gcd(i,j)μ(d)

转换枚举变量为d,令i=k1d,j=k2d

f(n)=d=1nμ(d)k1=1ndk2=1k1k1k2

g(n)=ni=1ni

则:

f(n)=d=1nμ(d)k1=1ndg(k1)

又令ϕ(n)=ni=1g(i)
则:

f(n)=d=1nμ(d)ϕ(nd)


故如果我们能预处理出ϕ(n)的值,我们就能用分块除法在O(Tn)的复杂度下解决问题了。

但直接分块维护ϕ(n)的复杂度是不可承受的O(nn)

然后打表找规律易发现,g(n)是可以由前一项递推的得到的值。
即:
g(n)=g(n1)+d(n1)+1
其中d(n)是 n的约数个数。

这样就可以O(n)预处理出g(n),然后O(n)预处理g(n)前缀和ϕ(n)了。
至此,大功告成。

总复杂度:O(n+Tn)

代码:

#include<cstdio>#include<cstring>#include<vector>using namespace std;typedef long long int ll;const ll mod = 1e9 + 7;const int A = 1e6 + 10;int mu[A],pri[A],tot;ll d[A],cnt[A],sum[A];bool vis[A];void init(){    tot = 0;mu[1] = d[1] = 1;    for(int i=2 ;i<A ;i++){        if(!vis[i]){mu[i] = -1;pri[++tot] = i;d[i] = 2;cnt[i] = 1;}        for(int j=1 ;j<=tot && pri[j]*i<A ;j++){            vis[i*pri[j]] = 1;            if(i%pri[j] == 0){                d[i*pri[j]] = d[i]/(cnt[i]+1)*(cnt[i]+2);                cnt[i*pri[j]] = cnt[i] + 1;                mu[i*pri[j]] = 0;                break;            }            d[i*pri[j]] = d[i]<<1;            cnt[i*pri[j]] = 1;            mu[i*pri[j]] = -mu[i];        }    }    sum[1] = 1;    for(int i=2 ;i<A ;i++){        sum[i] = (sum[i-1] + d[i-1] + 1)%mod;    }    for(int i=1 ;i<A ;i++){        sum[i] = (sum[i] + sum[i-1])%mod;        mu[i] = (mu[i] + mu[i-1])%mod;    }}int main(){    init();    int n;    while(~scanf("%d",&n)){        ll ans = 0;        int last;        for(int i=1 ;i<=n ;i=last + 1){            last = n/(n/i);            ans = (ans + (mu[last]-mu[i-1])%mod*(sum[n/i])%mod)%mod;        }        ans = (ans%mod + mod)%mod;        printf("%I64d\n",ans);    }    return 0;}
原创粉丝点击