洛谷 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<cstdio>
#include<iostream>
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;
}
在繁华中沉淀自我,在乱世中静静伫立,一笔一划,雕刻时光。