CodeForces 585D Present for Vitalik the Philatelist(容斥原理+gcd)

题意:给出一个数字序列,求有多少组满足原来子集不互质,先加一个元素后新集合互质的集合数量。

思路:首先解决子问题,如何求出一个集合中互质的子集个数,首先求出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;
}
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值