Codeforces 585E. Present for Vitalik the Philatelist 题解

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

题目大意:洛谷


题解:设\(g_i\)表示又多少个数与\(i\)互质,\(f_i\)表示有多少个集合的\(\gcd\)恰好为\(i\)。那么最终的答案就是\(\sum_{i\ge 2} f_i\times 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(n\ln n)\)求出,预处理后\(g_i\)也可以暴力在\(O(n\ln n)\)的时间内求出。

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

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

然后发现这是一个狄利克雷后缀和的形式,至于如何反过来,可以简单地理解为反着写就没问题了,总时间复杂度\(O(n\ln 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\n",ans);
	return 0;
}
posted @ 2020-08-31 18:40  with_hope  阅读(604)  评论(0)    收藏  举报