洛谷P3455 [POI2007]ZAP-Queries【莫比乌斯反演】

传送门

题解

这算是莫比乌斯反演的最简模板了吧。
首先手推公式,设:

\[f(d)=\sum_{x=1}^{N}\sum_{y=1}^{M}[gcd(x,y)=d]\qquad (N\le M) \]

根据 \(F(n)=\sum_{n|d}f(d)\),得:

\[F(n)=\bigg\lfloor\frac{N}{n}\bigg\rfloor\bigg\lfloor\frac{M}{n}\bigg\rfloor \]

根据莫比乌斯反演,得:

\[f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)=\sum_{n|d}\mu(\frac{d}{n})\bigg\lfloor\frac{N}{d}\bigg\rfloor\bigg\lfloor\frac{M}{d}\bigg\rfloor \]

枚举 \(t=\frac{d}{n}\),得:

\[f(n)=\sum_{t=1}^{\big\lfloor\frac{N}{n}\big\rfloor}\mu(t)\bigg\lfloor\frac{N}{nt}\bigg\rfloor\bigg\lfloor\frac{M}{nt}\bigg\rfloor \]

剩下的工作就要编程实现了,利用对 \(\mu\) 求前缀和与整除分块,就可以在 \(O(\sqrt{n})\) 以内回答每个问题了。
感觉莫比乌斯反演最难的还是通过 \(f(d)\) 正确得到 \(F(n)\)

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
typedef long long LL;
const int N=5e4+10;
int n,m,vis[N],prime[N],cnt,mu[N],sum[N],d;

void init(int n){
	mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]) {prime[++cnt]=i;mu[i]=-1;}
		for(int j=1;j<=cnt&&1ll*i*prime[j]<=n;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}

int main(){
	init(5e4);
	int T;scanf("%d",&T);
	while(T--){
		LL ans=0;
		scanf("%d%d%d",&n,&m,&d);
		n/=d,m/=d;
		for(int l=1,r;l<=min(n,m);l=r+1){
			r=min(n/(n/l),m/(m/l));
			if(r>min(n,m)) break;
			ans+=1ll*(sum[r]-sum[l-1])*(n/l)*(m/l);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-03-19 11:20  BakaCirno  阅读(90)  评论(0编辑  收藏  举报