版权声明:这是gigo写的QAQ https://blog.csdn.net/qq_42835815/article/details/84996065
YY的gcd啊,,题意如下;
多组数据
每组数据给出n,m,k;
求1<=i<=n,1<=j<=m,gcd(i,j)=k;的对数。
数据数最高为10000,n,m<=10000000
毒瘤数据。
根据上文所说,莫比乌斯反演最重要的第一步是找准f 和F两个函数。
明显本题f(d)指满足gcd(i,j)的对数。
那F的含义就如上文所示了。
所以
把f(p)代入得
反演一下得
可以看出这里我们在枚举质数,那我们换一下,枚举,并将F代入后半截得
为了方便表示,我们设T=dp
所以到现在为止,这个式子的复杂度已经是O(n)了。
但是由于是多组数据,所以我们要把复杂度降成O(根号下n);观察式子发现很多向下取整得到的值是一样的(在T不一样的时候),所以我们运用整除分块。整除分块后一坨一坨处理就需要一个前缀和sum。
整除分块很简单,,基本可以背代码,,大家请看代码就可以了。
#include<bits/stdc++.h>
#define in read()
using namespace std;
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();
if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}
return cnt*f;
}
int mul[10000003];
int prim[10000003],vis[10000003],cnt;
long long g[10000003],sum[10000003];//g与sum见mull处理区,自行理解ovo
int t;
void mull(int n){
mul[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prim[++cnt]=i;mul[i]=-1;
}
for(int j=1;j<=cnt&&prim[j]*i<=n;j++){
vis[prim[j]*i]=1;
if(i%prim[j]==0)break;
mul[prim[j]*i]=-mul[i];
}
}//筛完了mu
for(int j=1;j<=cnt;j++){
for(int i=1;i*prim[j]<=n;i++){
g[prim[j]*i]+=mul[i];//g[i]存最终式子的最后面那一坨
}
}
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+g[i];//g的前缀和
}
int main(){
t=in;int n,m;
mull(10000000);
while(t--){
n=in;m=in;
if(n>m)swap(n,m);
long long ans=0;
for(register int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));//注意,这里是整除分块。经典的n/(n/l)保证分出来的复杂度最接近根号n
ans+=1ll*(n/l)*(m/l)*(sum[r]-sum[l-1]);//前缀和的运用。
}
printf("%lld\n",ans);
}
return 0;
}