[Codeforces235E]Number Challenge(莫比乌斯反演)

题目描述

传送门

题解

看到这道题有没有想到sdoi的约数个数和?
没错真的是类似的

首先考虑 d(abc) 是多少
有一个结论:

d(abc)=i|aj|bk|c[(i,j)=1][(j,k)=1][(i,k)=1]

然后将这个式子带入
i=1aj=1bk=1cx|ay|bz|c[(x,y)=1][(y,z)=1][(x,z)=1]

=i=1aj=1bk=1caibjck[(i,j)=1][(j,k)=1][(i,k)=1]

然后利用反演公式 [n=1]=d|nμ(d) 将一个等式化开
=i=1aj=1bk=1caibjckd|(i,j)μ(d)[(j,k)=1][(i,k)=1]

=k=1cckd=1nμ(d)i=1a[d|i]ai[(i,k)=1]j=1b[d|j]bj[(j,k)=1]

i=di,j=dj
=k=1cckd=1nμ(d)i=1adadi[(i,k)=1]j=1bdbdj[(j,k)=1]

f(n,m)=i=1nni[(i,m)=1]
那么原式
=k=1cckd=1aμ(d)f(ad,k)f(bd,k)

f O(n) 的,那么总复杂度就是 O(n2logn)
优化常数的方法:gcd预处理/记忆化;因为abc的顺序是一定的无所谓的所以选两个小的枚举

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 2005
#define Mod 1073741824

int a,b,c,ans;
int p[N],prime[N],mu[N],G[N][N];

void get(int n)
{
    mu[1]=1;
    for (int i=2;i<=n;++i)
    {
        if (!p[i])
        {
            prime[++prime[0]]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j)
        {
            p[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else mu[i*prime[j]]=-mu[i];
        }
    }
}
void Min(int &a,int &b,int &c)
{
    if (a>b) swap(a,b);
    if (b>c) swap(b,c);
    if (a>b) swap(a,b);
}
int gcd(int a,int b)
{
    if (!b) return a;
    else return gcd(b,a%b);
}
int F(int n,int m)
{
    int ans=0;
    for (int i=1;i<=n;++i)
        if (G[i][m]==1)
            ans+=n/i;
    return ans;
}
int main()
{
    get(2000);
    scanf("%d%d%d",&a,&b,&c);
    Min(a,b,c);
    for (int i=1;i<=c;++i)
        for (int j=i;j<=c;++j)
            G[i][j]=G[j][i]=gcd(i,j);
    for (int i=1;i<=a;++i)
        for (int j=1;j<=min(b,c);++j)
            if (G[i][j]==1)
                ans+=mu[j]*(a/i)*F(b/j,i)*F(c/j,i);
    ans=(ans%Mod+Mod)%Mod;
    printf("%d\n",ans);
}

你可能感兴趣的:(题解,莫比乌斯反演)