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;
    }
    

    猜你喜欢

    转载自www.cnblogs.com/ZICLEX/p/10257619.html