初学莫比乌斯反演,翻了大量的题解才搞懂这题,所以决定自己写一个最详细的题解,虽然有些繁琐,但是每一步推导都十分详细。神犇就不要嘲讽我了2333
首先,我们定义
题目即要求
由于d是给定的,所以另
可以转化为
到这个地方的话题中所给的d就已经再没有用处了,不要和下文中的d混淆。
由莫比乌斯函数的性质即
将原本的和式转化为 ①,注意这里的d和题目中给定的d不是指的同一个东西。
然后根据性质, ②
再将和式提到前面,即。③
这一步怎么解释呢,当时我就一直卡在这里没弄懂。
hzwer:①式相当于,对于每个d,如果有对被d整除,答案就加上,
那么根据性质②,可以得到实际上就是(被整除的)*(被整除的),也就是
所以得到最后的式子是③式
但是这样直接做的话是O(n)的,所以我们要优化。
然后观察式(题)子(解)发现,在一段区间内是不会变化的,且最多有个取值,也是同理。
所以我们按取值来分成段,对维护一个前缀和,连续的一段直接计算就行了,具体参看代码。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define f(i,x,y) for (int i=x;i<=y;i++)
const int N=1e5+5;
int p[N+10],check[N+10],tot;
int mu[N],sum[N];
int T,n,m,d,ans;
void init(){
memset(check,1,sizeof check);
mu[1]=1;
f(i,2,N){
if (check[i]){
p[++tot]=i;
mu[i]=-1;
}
for (int j=1;j<=tot && p[j]*i<=N;j++){
check[i*p[j]]=0;
if (i%p[j]==0){
mu[i*p[j]]=0;
break;
}
else mu[i*p[j]]=-mu[i];
}
}
f(i,1,N) sum[i]=mu[i]+sum[i-1]; //维护前缀和
}
int calc(int n,int m){ //求[1,n][1,m]区间内互质的(x,y)的对数
int ret=0;
if (n>m) swap(n,m);
for (int L=1,R=0;L<=n;L=R+1){
R=min(n/(n/L),m/(m/L)); // 分段
ret+=(sum[R]-sum[L-1])*(n/L)*(m/L);
}
return ret;
}
int main(){
init();
scanf("%d",&T);
while (T--){
scanf("%d%d%d",&n,&m,&d);
ans=calc(n/d,m/d);
printf("%d\n",ans);
}
return 0;
}