Link: http://codeforces.com/contest/803/problem/F
题意:给n个数字,求有多少个GCD为1的子序列。
题解:容斥!比赛时能写出来真是炒鸡开森啊!
num[i]: 有多少个数字是 i 的倍数。
所有元素都是1的倍数的序列有:$2^n-1$个。先把$2^n-1$设为答案
所有元素都是质数的倍数的序列有:$sum 2^{num[p_1]} - 1$个,这些序列不存在的,得从答案中减去。
所有元素都是两质数之积的倍数的序列有:$sum 2^{num[p_1*p_2]} - 1$个,这些序列两次扫黄都在现
场,我们应减一次,但实际减了两次,多减了一次,所以要加回到答案中。
然后考虑,所有元素都是3,4,5......个质数之积的倍数的序列。
依次类推。于是就可以容斥了。
PS: 要先预处理好一个数字,能被拆成几个素数之积。而且同一个素数不能出现两次或以上。
【不优雅の】code:
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; typedef long long ll; const int MAXN = 100000+10; const int MAXM = 100000+10; ll prime[MAXN+1]; ll ct[MAXN], sgn[MAXN]; ll n, a[MAXN], bad[MAXN]; void getPrim() { memset(sgn, 1, sizeof(sgn)); memset(prime, 0, sizeof(prime)); for(int i=2;i<=MAXN;i++) { if(!prime[i]){ prime[++prime[0]] = i; } for(int j=1;(j<=prime[0])&&(prime[j]<=(MAXN/i));j++) { prime[prime[j]*i] = 1; if(i%prime[j]==0) break; } } } void getFactor(ll x) { ll cnt = 0, i; ll tmp = x; for(i = 1; prime[i] * prime[i] <=tmp ;i++) { if(tmp % prime[i] == 0) { int c = 0; while(tmp % prime[i] == 0) { c ++; tmp /= prime[i]; } if(c >= 2) { bad[x] = 1; return; } cnt ++; } } if(tmp!=1) { cnt ++; } ct[x] = cnt; } const ll MOD = 1000000007; ll mpow(ll a, ll n) { ll ret = 1; while(n) { if(n & 1) { ret = (ret * a); ret %= MOD; } a = a * a % MOD; n >>= 1; } return ret; } ll num[MAXN]; int main() { getPrim(); for(int i=1;i<MAXN;i++) { getFactor(i); } scanf("%lld", &n); for(int i=1;i<=n;i++) { scanf("%lld", &a[i]); for(ll j=1;j*j<=a[i];j++) { if(a[i]%j==0) { if(j*j!=a[i]) num[a[i]/j] ++; num[j] ++; } } } ll ans = 0; for(int i=2;i<MAXN;i++) { if(num[i]>0 && ct[i]>0 && !bad[i]) { //cout << i << " " << num[i] << " " << ct[i] << endl; ans += (ll)( (ct[i]%2==1)?(1):(-1) ) * (mpow(2, num[i])-1); ans %= MOD; } } ans = (mpow(2, n) - ans + MOD) % MOD; cout << (ans-1+MOD)%MOD << endl; }
官方题解提到了莫比乌斯函数,最终答案的表示为$sumlimits_{i=1}^{1e5} µ(i)(2^{num[i]}-1)$
套了下KuangBin巨巨的模板。重写了遍。
#include <iostream> #include <cstring> using namespace std; typedef long long LL; const int NICO = 100000+2; const int MOD = 1000000000 + 7; LL n, a[NICO], cnt[NICO], po[NICO], mo[NICO]; bool chk[NICO];int prime[NICO]; void init() { po[0] = 1, mo[1] = 1; for(int i=1;i<NICO;i++) po[i] = 2*po[i-1]%MOD; memset(chk, 0, sizeof(chk)); int tot = 0; for(int i=2;i<NICO;i++) { if(!chk[i]) { prime[tot++] = i; mo[i] = -1; } for(int j=0;j<tot;j++) { if(i*prime[j]>=NICO) break; chk[i*prime[j]] = 1; if(i%prime[j] == 0) { mo[i*prime[j]] = 0; break; } else { mo[i*prime[j]] = -mo[i]; } } } for(int i=1;i<=n;i++) { for(int j=1;j*j<=a[i];j++) { if(a[i]%j) continue; if(j*j != a[i]) cnt[a[i]/j] ++; cnt[j] ++; } } } int main() { scanf("%lld", &n); for(int i=1;i<=n;i++) scanf("%lld", &a[i]); LL ans = 0; init(); for(int i=1;i<NICO;i++) { ans = (ans + mo[i] * (po[cnt[i]]-1) )% MOD; } cout << (ans+1000LL*MOD)%MOD << endl; }