LG3455[POI2007]ZAP-Queries【整除分块+莫比乌斯反演】
前置知识:整除分块
整除分块
注:以下内容来自洛谷
题目大意
- 求$\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(x,y)=d]$
- 多组输入
- $1\le d\le a,b\le 50000$
解题思路
\[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)\]
\[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;
}