Codeforces 585E. Present for Vitalik the Philatelist 题解

题目链接:E. Present for Vitalik the Philatelist

题目大意:洛谷


题解:设(g_i)表示又多少个数与(i)互质,(f_i)表示有多少个集合的(gcd)恰好为(i)。那么最终的答案就是(sum_{ige 2} f_i imes g_i)

先考虑如何算(g),将式子列出来(令(c_i)表示(i)的出现次数):

[g_i=sum_{j=1} [gcd(i,j)= 1]c_j ]

[g_i=sum_{j=1} sum_{d|i,d|j} mu(d) c_j ]

[g_i=sum_{d|i} mu(d) sum_{d|j} c_j ]

然后发现后面的那一个式子(sum_{d|j} c_j)就是(d)的倍数出现次数,可以直接暴力(O(nln n))求出,预处理后(g_i)也可以暴力在(O(nln n))的时间内求出。

接下来考虑(f)(f)直接算不好算,所以考虑反演,设(f^{'}_i)为集合的(gcd)(i)的倍数的出现次数,那么可以得到。

[f^{'}_i=sum_{i|j} f_j ]

然后发现这是一个狄利克雷后缀和的形式,至于如何反过来,可以简单地理解为反着写就没问题了,总时间复杂度(O(nln n))(这里偷换了(n)的概念,所有和时间复杂度有关的(n)都是值域大小)。

代码:

#include <cstdio>
const int Maxn=500000;
const int Maxm=10000000;
const int Mod=1000000007;
int pow_2[Maxn+5];
bool np[Maxm+5];
int p[Maxm+5],p_len;
int mu[Maxm+5];
void init(){
	np[0]=np[1]=1;
	mu[1]=1;
	for(int i=2;i<=Maxm;i++){
		if(!np[i]){
			p[++p_len]=i;
			mu[i]=-1;
		}
		for(int j=1,x;j<=p_len&&(x=i*p[j])<=Maxm;j++){
			np[x]=1;
			mu[x]=-mu[i];
			if(i%p[j]==0){
				mu[x]=0;
				break;
			}
		}
	}
	pow_2[0]=1;
	for(int i=1;i<=Maxn;i++){
		pow_2[i]=(pow_2[i-1]<<1)%Mod;
	}
}
int a[Maxm+5];
int n;
int g[Maxm+5],f[Maxm+5];
int main(){
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		a[x]++;
	}
	for(int i=1;i<=p_len;i++){
		for(int j=Maxm/p[i];j>0;j--){
			a[j]+=a[j*p[i]];
		}
	}
	for(int i=1;i<=Maxm;i++){
		g[i]=a[i]*mu[i];
	}
	for(int i=1;i<=p_len;i++){
		for(int j=1;j*p[i]<=Maxm;j++){
			g[j*p[i]]+=g[j];
		}
	}
	for(int i=1;i<=Maxm;i++){
		f[i]=(pow_2[a[i]]-1+Mod)%Mod;
	}
	for(int i=p_len;i>0;i--){
		for(int j=1;j*p[i]<=Maxm;j++){
			f[j]=(f[j]-f[j*p[i]]+Mod)%Mod;
		}
	}
	int ans=0;
	for(int i=2;i<=Maxm;i++){
		ans=(ans+1ll*f[i]*g[i])%Mod;
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/withhope/p/13591105.html