[BZOJ 1101] POI 2007 Zap · 莫比乌斯 & 分块 超详细题解

初学莫比乌斯反演,翻了大量的题解才搞懂这题,所以决定自己写一个最详细的题解,虽然有些繁琐,但是每一步推导都十分详细。神犇就不要嘲讽我了2333

首先,我们定义

题目即要求

由于d是给定的,所以另

可以转化为

到这个地方的话题中所给的d就已经再没有用处了,不要和下文中的d混淆。

由莫比乌斯函数的性质即

将原本的和式转化为  ①,注意这里的d和题目中给定的d不是指的同一个东西。

然后根据性质, ②

再将和式提到前面,即。③

这一步怎么解释呢,当时我就一直卡在这里没弄懂。

hzwer:①式相当于,对于每个d,如果有被d整除,答案就加上

那么根据性质②,可以得到实际上就是(被整除的)*(被整除的),也就是

所以得到最后的式子是③式

但是这样直接做的话是O(n)的,所以我们要优化。

然后观察式(题)子(解)发现,在一段区间内是不会变化的,且最多有个取值,也是同理。

所以我们按取值来分成段,对维护一个前缀和,连续的一段直接计算就行了,具体参看代码。

#include 
#include 
#include 
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;
}


你可能感兴趣的:(BZOJ,莫比乌斯,数学)