1. 程式人生 > >BZOJ 1101: [POI2007]Zap(莫比烏斯反演)

BZOJ 1101: [POI2007]Zap(莫比烏斯反演)

傳送門

解題思路

  \[ \sum\limits_{i=1}^n\sum\limits_{j=1}^mgcd(i,j)=k \]

\[ \sum\limits_{i=1}^{\frac{n}{m}}\sum\limits_{j=1}^{\frac{m}{k}}gcd(i,j)=1 \]

\[ \sum\limits_{i=1}^{\frac{n}{k}}\sum\limits_{j=1}^{\frac{m}{k}}\sum\limits_{d|n,d|m}\mu(d) \]

\[ \sum\limits_{i=1}^{n}\mu(d)\sum\limits_{i=1}^{\frac{n}{kd}}\sum\limits_{j=1}^{\frac{m}{kd}} \]

  然後這樣就做完了,\(\mu\)搞一個字首和,其餘的整除分塊

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 50005;
typedef long long LL;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}

int n,miu[MAXN]={0,1},prime[MAXN],cnt,ans;
bool vis[MAXN];

int main(){
    for(int i=2;i<=50000;i++){
        if(!vis[i]) {prime[++cnt]=i;miu[i]=-1;}
        for(int j=1;j<=cnt && prime[j]*i<=50000;j++){
            vis[i*prime[j]]=1;
            if(!(i%prime[j])) {miu[i*prime[j]]=0;break;}
            miu[i*prime[j]]=-miu[i];
        }
    }
    for(int i=1;i<=50000;i++) miu[i]+=miu[i-1];
    n=rd();int a,b,d;
    while(n--){
        a=rd(),b=rd(),d=rd();if(a>b) swap(a,b);
        for(int l=1,r;l<=a/d;l=r+1){
            r=min((a/d)/(a/d/l),(b/d)/(b/l/d));
            ans+=(miu[r]-miu[l-1])*(a/l/d)*(b/l/d);
        }
        printf("%d\n",ans);ans=0;
    }
    return 0;
}