题意:给出一个数字序列,求有多少组满足原来子集不互质,先加一个元素后新集合互质的集合数量。
思路:首先解决子问题,如何求出一个集合中互质的子集个数,首先求出I的倍数的元素的个数,记为cnt[I],
然后运用容斥原理来做,当I为某些不重复的素数的组合时,当质因数的个数为奇时,减去2^cnt[I]-1,反之则加。
如果I的质因数分解的结果中有重复的质因数的情况,那么不加也不减,因为I已经在之前被考虑过了。
然后求出来互质的子集的数量后,固定每个元素,求出剩下的不互质的子集中与这个元素互质的集合数量就是答案,
因为这道题卡空间,即使预处理出每个数的所有质因数也会被卡掉,所以可以用筛法先求出每个数的最小的质因数,
求每个数的所有不同质因数组合成的因数时,先求出每个数的所有质因数
然后用状压求出每个数的所有不同质因数组合成的因数。
#include <bits/stdc++.h>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define pb push_back
#define mp make_pair
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int MAXN = 1000000;
const int MAXM = 10001000;
const int MOD = 1e9+7;
//const int INF = 0x3f3f3f3f;
int n;
int a[MAXN], cnt[MAXM], mul[1000], c[MAXM];
int sieve[MAXM];
LL po[MAXN];
void init(int m) {
for (int i = 1; i <= m; i++) sieve[i] = i;
for (int i = 2; i*i <= m; i++) if (sieve[i] == i) {
for (int j = i*i; j <= m; j+=i) sieve[j] = i;
}
po[0] = 1;
for (int i = 1; i <= n+10; i++) {
po[i] = po[i-1]<<1;
if (po[i] >= MOD) po[i] -= MOD;
}
}
vector<pii> get_prime(int n) {
vector<int> prime;
while (n > 1) {
int p = sieve[n];
while (n%p == 0) {
n /= p;
}
prime.push_back(p);
}
vector<pii> ans;
int sz = prime.size();
int maxS = 1 << sz;
mul[0] = 1;
for (int j = 0; j < sz; j++)
mul[1<<j] = prime[j];
for (int j = 1; j < maxS; j++) {
mul[j] = mul[j&-j] * mul[j^(j&-j)];
ans.push_back(mp(mul[j], __builtin_popcount(j)));
}
return ans;
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d", &n);
int maxA = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
maxA = max(a[i], maxA);
}
init(maxA+10);
for (int i = 1; i <= n; i++) {
vector<pii> d = get_prime(a[i]);
for (int j = 0; j < d.size(); j++) {
cnt[d[j].first]++;
c[d[j].first] = d[j].second;
}
}
LL cop = po[n] - 1;
for (int i = 2; i <= maxA; i++) {
if (c[i]&1)
cop = (cop-po[cnt[i]]+1) % MOD;
else if (c[i] != 0)
cop = (cop+po[cnt[i]]-1) % MOD;
//cout << i << " " << d.size() << " " << cop << endl;
}
LL noCop = po[n] - 1 - cop;
//cout << noCop << endl;
LL ans = 0;
for (int i = 1; i <= n; i++) {
vector<pii> d = get_prime(a[i]);
LL tmp = noCop;
for (int j = 0; j < d.size(); j++) {
if (d[j].second&1)
tmp = (tmp-po[cnt[d[j].first]]+1) % MOD;
else
tmp = (tmp+po[cnt[d[j].first]]-1) % MOD;
}
ans = (ans + tmp) % MOD;
//cout << ans << endl;
}
//cout << sieve[9] << endl;
ans = (ans+MOD) % MOD;
printf("%I64d", ans);
return 0;
}