题意:
k次询问,每次询问∑ni=1∑mj=1gcd(i,j)==d∑i=1n∑j=1mgcd(i,j)==d,即gcd(i,j)结果为d的个数。
k,n,m≤5e5。
题解:
原式显然可以化简为∑Ni=1∑Mj=1gcd(i,j)==1∑i=1N∑j=1Mgcd(i,j)==1,其中N=⌊nd⌋,M=⌊md⌋N=⌊nd⌋,M=⌊md⌋。
由于莫比乌斯函数有一个性质:
∑d∣nμ(d)={1,n=1.0,n≠1.∑d∣nμ(d)={0,n≠1.1,n=1.
n=1时,显然原式值为1;
n≠1时,设d=∏k1piaid=∏1kpiai,p为质数,a≠0,讨论每个d对答案的贡献。
- ∃ai≥2∃ai≥2,d对答案的贡献=0;
- ∀ai=1∀ai=1,d对答案的贡献=(k0)−(k1)+(k2)−(k3)+…(k0)−(k1)+(k2)−(k3)+…
根据二项式定理,当d不为1时对答案的贡献均为0,所以性质得证。
所以原式即可化为
∑i=1N∑j=1M∑d∣gcd(i,j)μ(d)∑i=1N∑j=1M∑d∣gcd(i,j)μ(d)
优先枚举d的取值,考虑d∣gcd(i,j)d∣gcd(i,j),可得(d∣i)∧(d∣j)(d∣i)∧(d∣j);
即μ(d)μ(d)被统计进答案,当且仅当i,ji,j均为dd的整数倍
所以答案为
由于⌊Nd⌋⌊Nd⌋,⌊Md⌋⌊Md⌋取值的阶梯性,我们可以在O(n−−√)O(n)的时间复杂度下枚举⌊Nd⌋⌊Nd⌋,⌊Md⌋⌊Md⌋的不同取值。
总时间复杂度O(nn−−√)O(nn)。
#include<cstdio>
#include<algorithm>
#define N 55000
#define mx 50000
using namespace std;
int mobius[N],prime[N],flag[N],tot=0,sum[N];
void sol(){
mobius[1]=1;
for(int i=2;i<=mx;i++){
if(!flag[i]){prime[tot++]=i;mobius[i]=-1;}
for(int j=0;j<tot;j++){
if(prime[j]*i>50000)break;
flag[prime[j]*i]=true;
if(i%prime[j]==0){mobius[prime[j]*i]=0;break;}
else mobius[prime[j]*i]=-mobius[i];
}
}
for(int i=1;i<=mx;i++)sum[i]=mobius[i]+sum[i-1];
return;
}
int main(){
int n;
scanf("%d",&n);
sol();
while(n--){
int n,m,d;
scanf("%d%d%d",&n,&m,&d);
n/=d,m/=d;
//sigma(mobius(d)*(n/d)*(m/d))
int ans=0,t;
for(int i=1;(n/i)&&(m/i);i=t+1){
t=min(n/(n/i),m/(m/i));
ans+=(sum[t]-sum[i-1])*(n/i)*(m/i);
}
printf("%d\n",ans);
}
}

本文详细解析了求解给定范围内所有i和j的组合中,使得gcd(i,j)==d的个数的问题。通过数学推导,利用莫比乌斯函数的性质简化了问题,并给出了具体的实现代码。


被折叠的 条评论
为什么被折叠?



