莫比乌斯反演-HDU5212

来源:互联网 发布:淘宝美工在线布局 编辑:程序博客网 时间:2024/06/08 05:03

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5212

题意:

给定序列,1≤i,j≤n,求gcd(a[i],a[j])∗(gcd(a[i],a[j])−1)之和。

分析:

同样我们设
f(d):满足gcd(x,y)=d且x,y均在给定范围内的(x,y)的对数。
F(d):满足d|gcd(x,y)且x,y均在给定范围内的(x,y)的对数。
反演后我们得到

这里写图片描述

由于序列给定,这里的F(d)我们可以通过枚举d,来找d的倍数的个数,那么F(d)=fen[d]∗fen[d],枚举最大公约数求出f(d),那么答案即为f(d)∗d∗(d−1)的和。时间复杂度O(nlogn)。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#define ll long longusing namespace std;const int maxn=10000;int prime[maxn+10],mu[maxn+10],isprime[maxn+10];int cnt=0;const int mod=10007;int a[maxn+10],num[maxn+10],fen[maxn+10];void init(){    memset(isprime,0,sizeof(isprime));    mu[1]=1;    for(int i=2; i<maxn; i++)    {        if(!isprime[i])        {            prime[cnt++]=i,mu[i]=-1;        }        for(int j=0; j<cnt&&prime[j]*i<maxn; j++)        {            isprime[prime[j]*i]=1;            if(i%prime[j]==0)            {                mu[prime[j]*i]=0;                break;            }            else            {                mu[i*prime[j]]=-mu[i];            }        }    }}int main(){    init();    int n;    while(~scanf("%d",&n))    {        memset(num,0,sizeof(num));        memset(fen,0,sizeof(fen));        int ma=0;        for(int i=0; i<n; i++)        {            scanf("%d",&a[i]);            num[a[i]]++;            ma=max(ma,a[i]);        }        //统计因子数 nlog(n)复杂度        for(int i=1; i<=ma; i++)            for(int j=i; j<=ma; j+=i)                fen[i]+=num[j];        ll ans=0;        for(int i=1; i<=ma; i++)        {            ll all=0;            for(int j=i; j<=ma; j+=i)            {                all+=(mu[j/i]*fen[j])%mod*fen[j]%mod;            }            ans=(ans+(all*i%mod)*(i-1))%mod;        }        printf("%I64d\n",ans);    }    return 0;}