题意:有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); } } }