莫比乌斯反演题目

持续更新ing

唔。从年前就想要搞,但是一直鸽到现在。

不过,就从现在开始吧!!!

前导知识

莫比乌斯反演定理两种形式的证明

目录

1.luogoP2568 GCD

2.luoguP2257 YY的GCD

 

1.luogoP2568 GCD 

题目链接

莫比乌斯反演的入门题。

思路:

ans=\sum _{i=1}^{n}\sum _{j=1}^{n}[gcd(i,j)=prim]=\sum _{d=1}^{n}[d=prim] \sum _{i=1}^{\frac{n}{d}}\sum _{j=1}^{\frac{n}{d}}[gcd(i,j)=1]

g\left ( n \right )=\sum _{i=1}^{n}\sum _{j=1}^{n}[gcd(i,j)=1]= \sum _{i=1}^{n}\sum _{j=1}^{n}\sum _{d|gcd(i,j)}u\left ( d \right )= \sum _{d=1}^{n}\left ( \frac{n}{d} \right )^2u(d)

ans=\sum _{d=1}^{n}[d=prim]g\left ( \frac{n}{d} \right )

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=1e7+100;
int n;
long long ans;
int vis[N],u[N],sum[N];
int p[N];
int cnt=0;
void seive(){
	vis[0]=1;
	vis[1]=1;
	u[1]=1;
	for(int i=2;i<=n;++i){
		if(!vis[i])p[cnt++]=i,u[i]=-1;
		for(int j=0;j<cnt&&(long long)p[j]*i<=n;++j){
			vis[p[j]*i]=1;
			u[p[j]*i]=-u[i];
			if(i%p[j]==0){
				u[p[j]*i]=0;
				break;
			}
		}
	}
	for(int i=1;i<=n;++i)sum[i]=sum[i-1]+u[i];
}
long long g(int n){
	long long ans=0;
	for(int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=(long long)(n/l)*(n/l)*(sum[r]-sum[l-1]);
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	seive();
	ans=0;
	for(int i=0;i<cnt;++i)
		ans+=g(n/p[i]);
	printf("%lld\n",ans);
	return 0;
} 

2.luoguP2257 YY的GCD

题目链接

思路:

设    f\left ( n \right )=\sum _{n|d}[gcd\left ( i,j \right )=n]  ,F\left ( n \right )=\sum _{n|d}f\left ( d \right )=\frac{N}{n}*\frac{M}{n}

ans=\sum _{i=1}^{N}\sum _{j=1}^{M}[gcd(i,j)\in \mathbb{P}]=\sum _{n=1,n\in \mathbb{P}}^{min\left ( N,M \right )} \sum _{i=1}^{N}\sum _{j=1}^{M}[gcd(i,j)=n]=\sum _{n=1,n\in \mathbb{P}}^{min\left ( N,M \right )}f\left ( n \right )

根据莫比乌斯反演定理F\left ( n \right )=\sum _{n|d}f\left ( d \right )     =>     f\left ( n \right )=\sum _{n|d}u\left ( \frac{d}{n} \right )F\left ( d \right ) 得,

ans=\sum _{n=1,n\in \mathbb{P}}^{min\left ( N,M \right )}\sum _{n|d}u\left ( \frac{d}{n} \right )F\left ( d \right )=\sum _{n=1,n\in \mathbb{P}}^{min\left ( N,M \right )}\sum _{n|d}u\left ( \frac{d}{n} \right )\frac{N}{d}\frac{M}{d}

=\sum_{d=1}^{N}\frac{N}{d}\frac{M}{d}\sum _{n|d,n\in \mathbb{P}}u\left ( \frac{d}{n} \right )

g\left ( d \right )=\sum _{n|d,n\in \mathbb{P}}u\left ( \frac{d}{n} \right )

筛的时候先O\left ( n \right )筛出来u\left ( n \right ),再求g\left ( n \right ),求g\left ( n \right )得时候时间复杂度跟埃氏筛差不多。再对g\left ( n \right )求前缀和。

然后就是整除分块的套路写法了。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e7+100;
int vis[N],u[N],p[N];
long long g[N],sum[N],ans;
int cnt,t,n,m;
void seive(){
	vis[1]=1;
	u[1]=1;
	for(int i=2;i<N;++i){
		if(!vis[i]){
			p[cnt++]=i;
			u[i]=-1;
		}
		for(int j=0;j<cnt&&(long long)p[j]*i<N;++j){
			vis[p[j]*i]=1;
			u[p[j]*i]=-u[i];
			if(i%p[j]==0){
				u[p[j]*i]=0;
				break;
			}
		}
	}
	for(int i=0;i<cnt;++i)
		for(int j=1;(long long)p[i]*j<N;++j)
			g[p[i]*j]+=u[j];
	for(int i=1;i<N;++i)sum[i]=sum[i-1]+g[i];
}
long long cal(int n,int m){
	long long ans=0;
	for(int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(long long)(n/l)*(m/l)*(sum[r]-sum[l-1]);
	}
	return ans;
}
int main(){
	seive();
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		if(n>m)n^=m^=n^=m;
		ans=cal(n,m);
		printf("%lld\n",ans);
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值