和Leo一起做爱数学的好孩子HDU5382 GCD?LCM!

版权声明:LeoJAM Presents https://blog.csdn.net/fcb_x/article/details/82779081

超级毒瘤的反演

F_{n}=\sum_{i=1}^{n}\sum_{j=1}^{n}\left [ LCM(i,j)+GCD(i,j)>=n \right ]

我们发现这个外层嵌套了所以考虑递归

引理:

[LCM(i,j)+GCD(i,j)>=n] 在 (i+j)>=n+1 恒成立

这好理解,假设及时GCD就是较小的,那么值也是i+j

所以递归式为:

F_{i}=F_{i-1}+2*i-1-(\sum_{i=1}^{n}\sum_{j=1}^{n}[(GCD(i,j)+LCM(i,j))==(n-1)])

不妨:设T_{i}=\sum_{i=1}^{n}\sum_{j=1}^{n}[GCD(i,j)+LCM(i,j)==n]

稍有常识的OI选手都知道

LCM_{i,j}=\frac{i*j}{GCD_{i,j}}

带入:T_{i}=\sum_{i=1}^{n}\sum_{j=1}^{n}[GCD(i,j)+\frac{i*j}{GCD(i,j)}==n]

枚举GCD

T_{i}=\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{d=1}^{n}[d+\frac{i*j}{d}==n]*[GCD(i,j)==d]

交换枚举顺序

T_{i}=\sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}[i*j*d+d==n]*[GCD(i,j)==1]

观察右式发现d|n

减少枚举数量

T_{i}=\sum_{d|n}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}[i*j==\frac{n}{d}-1][GCD(i,j)==1]

再次发现不需要枚举j

T_{i}=\sum_{d|n}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}[GCD(i,\frac{\frac{n}{d}-1}{i})==1]

发现时间有三秒而这个是一个调和级数

所以埃式筛就好了

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
const int mod=258280327;
int vis[N];
int Prime[N];
int G[N];
int T[N];
int F[N];
int S[N];
int cnt=0;
void Pre(){
	G[1]=1;
	for(int i=2;i<N;++i){
		if(!vis[i]){
			Prime[++cnt]=i;
			G[i]=2;
		}
		for(int j=1;j<=cnt&&Prime[j]*i<N;++j){
			vis[i*Prime[j]]=1;
			if(i%Prime[j]==0){
				G[i*Prime[j]]=G[i];
				break;
			}
			G[i*Prime[j]]=G[i]*2;
		}
	}
	for(int i=1;i<N;++i){
		for(int j=i;j<N;j+=i){
			T[j]=(T[j]+G[i-1])%mod;
		}
	}
	for(int i=1;i<N;++i){
		F[i]=(((F[i-1]+2*i-1-T[i-1])%mod)+mod)%mod;
	}
	for(int i=1;i<N;++i){
		S[i]=(S[i-1]+F[i])%mod;
	}
}
int main(){
//	freopen("test.in","r",stdin);
	Pre();
	int T;
	scanf("%d",&T);
	while(T--){
		int x,y;
		scanf("%d",&x);
		cout<<S[x]<<'\n';
	}
}

猜你喜欢

转载自blog.csdn.net/fcb_x/article/details/82779081