莫比乌斯反演×bitset好题。
看到集合又只关心奇偶性所以想到可以用bitset维护,(1) 代表奇数,(0) 代表偶数。
直接维护每个数出现的奇偶性是不好搞 (3) 操作的。所以我们考虑维护每个数的倍数出现的次数,记为 (f(x))。即假设每个数出现次数为 (g(x)),(f(x)=sum_{x|y}g(y))。
那么根据莫比乌斯反演可得:(g(x)=sum_{x|y}mu(frac{y}{x})f(y))。我们处理出所有 (x) 的倍数的位置,(mu) 函数在模 (2) 的意义下只有两种取值,将这些位置赋值为这些值,预处理出bitset,然后与当前集合的bitset相乘,在模 (2) 的意义下即为与,最后 (1) 的个数的奇偶性即为答案。
再考虑 (1,2,3) 操作。
对于 (1) 操作,也可以预处理,不过原先的要先清空,然后再或起来。
对于 (2) 操作,如果不模 (2) 就是相加,所以模 (2) 意义下直接异或就好。
对于 (3) 操作,我们发现在原来就是两者相乘,于是现在模 (2) 意义下变成了与。
这样这道题就解决了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100010;
const int MAXV = 7010;
template <typename T> void read(T &x) {
T f = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= f;
}
bitset<MAXV> S[MAXN], d[MAXV], t[MAXV];
int n, q;
int prime[MAXV], cnt;
int mu[MAXV];
bool mark[MAXV];
void sieve() {
mu[1] = 1;
for (int i = 2; i <= 7000; i++) {
if (!mark[i]) {
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && prime[j] * i <= 7000; j++) {
mark[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
mu[i * prime[j]] = -mu[i];
}
}
}
int main() {
read(n); read(q);
sieve();
for (int i = 1; i <= 7000; i++) for (int j = i; j <= 7000; j += i) d[i].set(j, (mu[j / i] + 2) % 2), t[j].set(i);
while (q--) {
int opt, x, y, z, v;
read(opt); read(x);
if (opt == 1) {
read(v);
S[x].reset();
S[x] |= t[v];
} else if (opt == 2) {
read(y); read(z);
S[x] = S[y] ^ S[z];
} else if (opt == 3) {
read(y); read(z);
S[x] = S[y] & S[z];
} else {
read(v);
printf("%d", (int)(d[v] & S[x]).count() % 2);
}
}
return 0;
}