题意
给出一个数集,求所有非空子集的权值和。
定义一个集合的权值为:若所有元素的gcd=1则权值为0,否则权值为所有元素的gcd乘以集合大小。
n≤100000,数字范围≤1000000
题解
其实是道挺简单的题目,但是因为之前没做过类似的
gcd
的容斥的题,所以当时没做出来。
有这样一个显然的东西:考虑所有d的倍数的数构成的集合,其任意一个子集的所以数
gcd
一定为
d,d∗2,d∗3...
根据这个原理就可以容斥了。
设
res[i]
为所有
gcd
等于
i
的集合的长度的和。最后答案就是
怎么求
res[i]
?
res[i]=[i的倍数的数构成的集合的所有子集的大小和]−res[i∗2]−res[i∗3]−res[i∗4]...
记是
i
的倍数的数有
∑i=1ki×(ki)=∑i=1ki∗k!i!∗(k−i)!=∑i=1kk×(k−1i−1)=k∗2k−1
然后直接做就好了。复杂度就是那个调和级数,得 O(nlogn) 。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxv=1000005,MOD=1000000007;
typedef long long LL;
int n,m,a[maxv],cnt[maxv],res[maxv],ans;
LL pw2[maxv];
int main(){
//freopen("cf839D.in","r",stdin);
//freopen("cf839D.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x; scanf("%d",&x);
m=max(m,x); a[x]++;
}
pw2[0]=1; for(int i=1;i<=n;i++) pw2[i]=pw2[i-1]*2%MOD;
for(int i=2;i<=m;i++)
for(int j=1;(LL)i*j<=m;j++) cnt[i]+=a[i*j];
for(int i=m;i>=2;i--){
res[i]=pw2[cnt[i]-1]*cnt[i]%MOD;
for(int j=2;(LL)i*j<=m;j++) res[i]=(res[i]+(MOD-res[i*j]))%MOD;
ans=(ans+((LL)res[i]*i)%MOD)%MOD;
}
printf("%d\n",ans);
return 0;
}