P1447 [NOI2010]能量采集

传送门

显然答案就是 $\sum_{i=1}^{n}\sum_{j=1}^{m}(2gcd(i,j)-1)$

把合式里面的系数 $2$ 和常数 $+1$ 提出来,变成 $2\sum_{i=1}^{n}\sum_{j=1}^{m}(gcd(i,j))-nm$

看到了有趣的 $gcd$ ,考虑莫比乌斯反演

设 $f[x]=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==x]$

和 $F[x]=\sum_{i=1}^{n}\sum_{j=1}^{m}[x|gcd(i,j)]$

那么显然有 $F[x]=\sum_{x|d}f[d]$

然后考虑 $F[x]$ 的意义,其实就是对于 $i \in [1,n]\ ,\ j \in [1,m]$ 的所有整数对 $i,j$,他们同时有一个因数为 $x$ ,的数对 $i,j$ 的数量

显然有且只有 $x$ 的倍数有因数 $x$,所以对于 $[1,n]$ 的整数,有 $\left \lfloor \frac{n}{x} \right \rfloor$ 个数有因数 $x$

同理对于 $[1,m]$ 的整数,有 $\left \lfloor \frac{m}{x} \right \rfloor$ 个数有因数

这两组数可以任意两两组合都合法,所以

$F[x]=\left \lfloor \frac{n}{x} \right \rfloor\left \lfloor \frac{m}{x} \right \rfloor$

然后根据莫比乌斯反演的套路:如果 $F[x]=\sum_{x|d}f[d]$,那么 $f[x]=\sum_{d|x}u(\frac{d}{x})F[d]$

所以 $f[x]=\sum_{d|x}u(\frac{d}{x})F[d]$,直接预处理 $F$ 就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e6+7;
int n,m,mx,pri[N],mu[N],tot;
ll f[N],F[N],Ans;
bool not_pri[N];
void pre()
{
    not_pri[1]=1; mu[1]=1;
    for(int i=2;i<=mx;i++)
    {
        if(!not_pri[i]) pri[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot;j++)
        {
            ll t=1ll*i*pri[j]; if(t>mx) break;
            not_pri[t]=1; if(!(i%pri[j])) break;
            mu[t]=-mu[i];
        }
    }
}
int main()
{
    n=read(),m=read(); mx=max(n,m);
    pre();
    for(int i=1;i<=mx;i++) F[i]=1ll*(n/i)*(m/i);
    for(int i=1;i<=mx;i++)
    {
        for(int j=i;j<=mx;j+=i) f[i]+=mu[j/i]*F[j];
        Ans+=f[i]*i;
    }
    Ans<<=1; Ans-=1ll*n*m;
    printf("%lld\n",Ans);
    return 0;
}

 

posted @ 2019-06-19 21:46  LLTYYC  阅读(230)  评论(0编辑  收藏  举报