[BZOJ2045]双亲数(莫比乌斯反演)

题目描述

传送门

题解

题目要求

i=1aj=1b[(i,j)=k]

变形
i=1aj=1b[k|i][k|j][(ik,jk)=1]

令i=ik,j=jk得
i=1akj=1bk[(i,j)=1]

我们设f(n)为最大公约数为n的数对个数,F(n)为公约数为n的数对个数,即最大公约数为n的倍数的数的个数,则f(1)就是我们要求的答案。
存在关系

F(n)=n|df(d)

利用莫比乌斯反演公式可以得到
f(n)=n|dμ(d)F(dn)

那么接下来的问题就是求解F(n)

由F(n)的定义可以得到公式

F(n)=i=1aj=1b[n|(i,j)]

其实也就是[1,a][1,b]范围内可以同时整除n的数对个数
设[1,a]内可以整除n的数个数为p,[1,b]内可以整除n的数的个数为q,那么由乘法原理可得F(n)=p*q
那么利用结论
p=an,q=bn

可以得到
F(n)=anbn

那么我们现在就可以O(1)得到F(n)的函数值

那么我们回到原来的公式

f(n)=n|dμ(d)F(dn)

我们要求解f(1),那么公式变成了
f(1)=μ(d)F(d)

那么我们只要枚举d就可以了
看上去d好像是没有上限的,但是仔细观察公式 F(n)=anbn 可以知道,当枚举的d>min(a,b)时,F(n)的函数值为0,就不会再计算了。所以枚举d的上限为min(a,b)
这里的 μ 是可以线性筛出来的,F(n)是可以O(1)算出来的,算法的思路就很清晰了。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int N=1e6;

int T,a,b,k;
LL ans,re;
int mu[N+5],p[N+5],prime[N+5];

inline void get_mu(){
    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];
        }
    }
}
inline LL F(int n){
    return (LL)(a/n)*(b/n);
}
int main(){
    get_mu();
    scanf("%d%d%d",&a,&b,&k);
    if (k==0){printf("%d\n",0);return 0;}
    a/=k; b/=k; if (a>b) swap(a,b);
    ans=0;
    for (int i=1;i<=a;++i) ans+=mu[i]*F(i);
    printf("%lld\n",ans);
}

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