洛谷P2257 YY的GCD【莫比乌斯反演】

传送门

题解

和前一道模板题类似,先设 \(f(d)\),再得 \(F(n)\),然后反演 \(f(n)\)。因为最终答案:

\[Ans=\sum_{n\in prime}f(n)=\sum_{n\in prime}\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 \]

还可以继续处理,但这里我还有点想不明白,可以先枚举 \(T=nt\)

\[Ans=\sum_{T=1}^{N}\bigg\lfloor\frac{N}{T}\bigg\rfloor\bigg\lfloor\frac{M}{T}\bigg\rfloor\sum_{n|T,n\in prime}\mu(\frac{T}{n}) \]

后面那一坨就可以在求 \(\mu\) 之后处理出来,然后算 \(Ans\),就与前一道模板题差不多了,预处理后面那一坨得前缀和,然后每次询问用整除分块计算。

代码

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

void euler(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;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<=cnt;i++)
		for(int j=1;1ll*j*prime[i]<=n;j++)
			sum[j*prime[i]]+=mu[j];
	for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
}

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