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

思路

和YY的GCD类似但是更加简单了
类似的推一波公式即可

\[F(n)=\sum_{n|d}f(d) \]

\[f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d) \]

\[F(d)=\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor \]

\[f(x)=\sum_{x|d}\mu(\frac{d}{x})\times\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor \]

\[f(x)=\sum_{t=1}^{\lfloor\frac{n}{x}\rfloor}\mu(t)\times\lfloor\frac{n}{x\times t}\rfloor\times\lfloor\frac{m}{x\times t}\rfloor \]

然后整除分块即可

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,n,m,mu[51000],iprime[51000],isprime[51000],summu[51000],cnt,k;
void prime(int n){
    isprime[1]=true;
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(!isprime[i])
            iprime[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&iprime[j]*i<=n;j++){
            isprime[iprime[j]*i]=true;
            mu[iprime[j]*i]=-mu[i];
            if(i%iprime[j]==0){
                mu[iprime[j]*i]=0;
                break;
            }
        }
    }
    for(int i=1;i<=n;i++)
        summu[i]=summu[i-1]+mu[i];
}
long long f(int k){
    long long ans=0;
    for(int l=1,r;l<=min(n,m);l=r+1){
        r=min((n/(n/(l))),(m/(m/(l))));
        ans+=1LL*(summu[r]-summu[l-1])*(n/(l*k))*(m/(l*k));
        }
    return ans;
}
int main(){
    prime(50100);
    scanf("%d",&T);
    while(T--){
        scanf("%d %d %d",&n,&m,&k);
        if(n<m)
            swap(n,m);
        printf("%lld\n",f(k));
    }
    return 0;
}
posted @ 2018-12-04 21:13  dreagonm  阅读(103)  评论(0编辑  收藏  举报