Codeforces 1097F Alex and a TV Show (莫比乌斯反演)

题意:有n个可重集合,有四种操作:

1:把一个集合设置为单个元素v。

2:两个集合求并集。

3:两个集合中的元素两两求gcd,然后这些gcd形成一个集合。

4:问某个可重复集合的元素v的个数取模2之后是多少。

思路:因为集合的元素是对2取模,那么我们可以用bitset来代替可重复集合。但是,如果每个bitset来直接代表多重集的话,第三个操作会很麻烦。所以我们每个集合的bitset用来代表每个元素和每个元素约数的集合,这样某个约数为标记为1说明这个约数的倍数的和为奇数(这个约数的倍数就是这个元素)这样第三个操作实际上就是两个bitset互相AND。为什么呢?新的集合数x是奇数,只有当第一个集合和第二个集合的x也是奇数个才行。那么这种情况下怎么知道某个集合的元素v有多少个呢?我们令f(n)为询问的集合元素n的个数,g(d)为询问的集合中d及其倍数的元素的个数,那么我们可列出表达式:g(d) = ∑(d|n) (f(n)),我们反演一下,可得f(d) = ∑(d|n)(mo(n / d) * g(n)) (mo是莫比乌斯函数)。因为答案取模2,而在%2意义下-1和1是一样的,所以我们可以预处理出来n的数中哪些莫比乌斯函数值是1,哪些是0,存在一个bitset里。g(n)囊括在了每个多重集的bitset里了,那么把这个两个bitsetAND一下,算一下1的个数取模2,就是最终答案。

代码和思路参考了这篇博客:https://www.cnblogs.com/cjyyb/p/10283115.html

代码:

#include <bits/stdc++.h>
#define bit bitset<7001>
using namespace std;倍数
const int maxn = 100010;
bit a[maxn], b[7001], mup[7001], f[7001];
bool mu[7001];
void init() {
	mu[1] = 1;
	for (int i = 1; i <= 7000; i++)
		for (int j = i + i; j <= 7000; j += i)
			mu[j] -= mu[i];
	for (int i = 1; i <= 7000; i++)
		for (int j = i; j <= 7000; j += i)
			f[j][i] = 1;
	for (int i = 1; i <= 7000; i++)
		for (int j = i; j <= 7000; j += i)
			if(mu[j / i]) mup[i][j] = 1;
}
int main() {
	int n, m, op, x, y, z;
	init();
	scanf("%d%d", &n, &m);
	while(m--){
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d%d", &x, &y);
			a[x] = f[y];
		} else if(op == 2) {
			scanf("%d%d%d", &x, &y, &z);
			a[x] = a[y] ^ a[z];
		} else if(op == 3) {
			scanf("%d%d%d", &x, &y, &z);
			a[x] = a[y] & a[z];
		} else {
			scanf("%d%d", &x, &y);
			printf("%d", (a[x] & mup[y]).count() & 1);
		}
	}
}

  

猜你喜欢

转载自www.cnblogs.com/pkgunboat/p/10519903.html