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

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

浙公网安备 33010602011771号