题目链接: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;
}