HDU5382 GCD?LCM!

超级毒瘤的反演

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';
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值