洛谷P3455 [POI2007]ZAP-Queries (莫比乌斯反演)

题意:求$\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)==d]$(1<=a,b,d<=50000)。

很套路的莫比乌斯反演。

$\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==k]=\sum_{i=1}^{\lfloor \frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{k}\rfloor}[gcd(i,j)==1]$

令f(n)为gcd是n的个数,g(n)为gcd是n或n的倍数的个数。

根据反演公式可以得到:$f(n)=\sum_{n|d}\mu(\frac{d}{n})g(d)$

答案即为f(1),对于g函数可以O(1)得到答案,$g(d)=\lfloor \frac{n}{d}\rfloor\lfloor \frac{m}{d}\rfloor$
原式$=\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d){\lfloor \frac{n}{kd}\rfloor}{\lfloor \frac{m}{kd}\rfloor}$

预处理莫比乌斯函数前缀和,后面部分整除分块就行了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+5;
int pri[N],tot,mu[N];
bool p[N];
void init() {
    mu[1]=1;
    for(int i=2;i<N;i++) {
        if(!p[i]) mu[i]=-1,pri[tot++]=i;
        for(int j=0;j<tot&&pri[j]*i<N;j++) {
            p[i*pri[j]]=true;
            if(i%pri[j]==0) {
                mu[i*pri[j]]=0;
                break;
            }
            else mu[i*pri[j]]=-mu[i];
        }
    }
    for(int i=1;i<N;i++) mu[i]+=mu[i-1];
}
int main() {
    init();
    int T,n,m,d;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d%d",&n,&m,&d),n/=d,m/=d;
        if(n>m) swap(n,m);
        ll ans=0;
        for(int l=1,r;l<=n;l=r+1) {
            r=min(n/(n/l),m/(m/l));
            ans+=1LL*(mu[r]-mu[l-1])*(n/l)*(m/l);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2019-09-18 18:36  zdragon  阅读(178)  评论(0编辑  收藏  举报