洛谷 P3455&BZOJ1101 【[POI2007]ZAP-Queries】

这应该是入坑莫比乌斯反演的第一道题了吧

其实题目让我们求的东西很简单,就是
\[ ans=\sum_{i=1}^{a}\sum_{j=1}^{b}\left [ gcd(i,j)=k \right ]\]

然后,显然,我们可以再化简一下,其实刚刚的式子就等价于
\[ans=\sum_{i=1}^{a/k}\sum_{j=1}^{b/k}\left [ gcd(i,j)=1 \right ]\]

但是,显然这个东西是十分不好算的

因为这是一道莫比乌斯反演的经典题,所以我们可以套一套
不妨设
\[f(x)=\sum_{i=1}^{a/k}\sum_{j=1}^{b/k}\left [ gcd(i,j)=x \right ]\]

那么,显然ans=f(1)

又可以设

\[g(x)=\sum_{i=1}^{a/k}\sum_{j=1}^{b/k}\left [ x|gcd(i,j) \right ]\]

这东西显然就等于

\[\left \lfloor \frac{a}{kx} \right \rfloor*\left \lfloor \frac{b}{kx} \right \rfloor\]

由两个函数的定义便可以证得

\[g(x)=\sum_{x|k,x<=n}^{}f(x)\]

然后就是熟悉的味道了

具体见代码

#include
#include
using namespace std;
long long maxn=1e5+10;
long long miu[100010],vis[100010];
void mobius()
{
    for(int i=1;i<=maxn;++i)
        miu[i]=1;
    for(int i=2;i<=maxn;++i)
    {
        if(!vis[i])
        {
            miu[i]=-1;
            for(int j=i+i;j<=maxn;j+=i)
            {
                vis[j]=1;
                if((j/i)%i==0) miu[j]=0;
                else miu[j]*=-1;
            }
        }
    }
    for(int i=1;i<=maxn;++i)
        miu[i]+=miu[i-1];
}
int main()
{
    mobius();
    int T;
    int a,b,k;
    
    scanf("%lld",&T);
    for(long long _=1;_<=T;++_)
    {
        long long ans=0;
        scanf("%d%d%d",&a,&b,&k);
        int tmp=min(a,b);
        int r;
        for(int l=1;l<=tmp;l=r+1)
        {

            r=min(a/(a/l),b/(b/l));
            ans=ans+(miu[r]-miu[l-1])*(a/(l*k))*(b/(l*k));
        }
        printf("%lld\n",ans);
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/HenryHuang-Never-Settle/p/10478803.html

你可能感兴趣的:(洛谷 P3455&BZOJ1101 【[POI2007]ZAP-Queries】)