1. 程式人生 > >LG3455[POI2007]ZAP-Queries【整除分塊+莫比烏斯反演】

LG3455[POI2007]ZAP-Queries【整除分塊+莫比烏斯反演】

LG3455[POI2007]ZAP-Queries【整除分塊+莫比烏斯反演】

前置知識:整除分塊

整除分塊


注:以下內容來自洛谷

題目大意

  • 求$\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(x,y)=d]$
  • 多組輸入
  • $1\le d\le a,b\le 50000$

解題思路

  • 根據之前做過的題的經驗(YY的GCD),那麼這一題就顯得十分套路(簡單)了。
  • 數論函式

  • 我們設:
    \[f(k)=\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)=k]\]
    \[F(n)=\sum_{n|k}f(k)=\lfloor\frac{a}{n}\rfloor\lfloor\frac{b}{n}\rfloor\]

    則可以由莫比烏斯反演可以推出:
    \[f(n)=\sum_{n|k}\mu(\lfloor\frac{k}{n}\rfloor)F(k)\]

  • (PS:如果不知道為什麼要設這兩個函式,可以點開我上面放的連結)

  • 設完這兩個函式之後,我們便驚喜的發現,\(Ans=f(d)\)

  • 於是就直接開始推答案:
    \[Ans=\sum_{d|k}\mu(\lfloor\frac{k}{d}\rfloor)F(k)\]
    列舉\(\lfloor\frac{k}{d}\rfloor\)設為\(t\)
    \[Ans=\sum_{t=1}^{min(a,b)}\mu(t)\lfloor\frac{a}{td}\rfloor\lfloor\frac{b}{td}\rfloor\]

    這時候,這個式子已經可以做到\(O(n)\)的時間複雜度了,但是因為有多組資料,所以我們再用一下整除分塊,這題就可以做到\(O(\sqrt{n})\)了。
  • 貼上程式碼:

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int MX=50005,N=50505;
    inline void Read(int &x){
        x=0;register char cc='\0';int fff=1;
        for(;cc<'0'||cc>'9';cc=getchar())if(cc=='-')fff=-1;
        for(;cc>='0'&&cc<='9';cc=getchar())x=(x<<1)+(x<<3)+(cc&15);
        x*=fff;
    }
    bool Pr[N];
    int n,a,b,d,tot,p[N],mu[N];
    void Prepare(){
        Pr[0]=Pr[1]=true;mu[1]=1;
        for(int i=2;i<=MX;++i){
            if(!Pr[i]){p[++tot]=i;mu[i]=-1;}
            for(int j=1;j<=tot&&i*p[j]<=MX;++j){
                Pr[i*p[j]]=true;
                if(i%p[j]==0)break;
                mu[i*p[j]]=mu[i]*mu[p[j]];
            }
        }
        for(int i=2;i<=MX;++i)
            mu[i]+=mu[i-1];
    }
    int main()
    {
        freopen("LG3455.in","r",stdin);
        freopen("LG3455.out","w",stdout);
        Read(n);
        Prepare();
        while(n--){
            Read(a);Read(b);Read(d);
            a/=d;b/=d;if(a>b)swap(a,b);
            long long ans=0;
            for(int i=1,j=1;i<=a;i=j+1){
                j=min(a/(a/i),b/(b/i));
                ans+=1LL*(mu[j]-mu[i-1])*(a/i)*(b/i);
            } 
            printf("%lld\n",ans);       
        }
        return 0;
    }