CF1097F Alex and a TV Show
题目大意
有 (n) 个可重集,初始时均为空。接下来进行 (q) 次操作。操作有如下四种:
- (1 x v),将第 (x) 个可重集设为 ({v})。
- (2 x y z),将第 (x) 个可重集设为第 (y) 个可重集和第 (z) 个可重集的并。
- (3 x y z),将第 (x) 个可重集设为第 (y) 个可重集和第 (z) 个可重集的乘积。两个可重集 (A,B) 的乘法定义为:(A imes B = { gcd(a, b)\, mid\, a in A,\, b in B }),结果也是一个可重集。
- (4 x v)。求第 (x) 个可重集里数值 (v) 出现的次数在 (mod 2) 意义下的结果。
(x, y, z) 可能相同。
数据范围:(1leq nleq 10^5),(1leq qleq 10^6),(1leq vleq 7000)。
本题题解
设 (d) 表示涉及数值的最大值,本题里 (dleq 7000)。
因为只需要求 (mod 2) 意义下的答案,容易想到用 ( exttt{bitset}) 存储每个值出现的次数的奇偶性。具体来说,设 (f_i(x)) 表示在第 (i) 个可重集里,数值 (x) 出现的次数的奇偶性。那么操作 2 就是将两个 ( exttt{bitset}) 做 (operatorname{xor}),单次操作的时间复杂度为 (mathcal{O}(frac{d}{omega})),其中 (omega) 是位长,可以认为 (omega = 64)。但是操作 3 则难以快速实现。
考虑 FFT 算法的思想,将难以计算的东西转化为“点值”,对点值进行运算,再通过逆操作还原回去。设 (g_i(x) = (sum_{x | y} f_i(y))mod 2),也就是【(x) 的所有倍数的出现次数之和】的奇偶性。注意到,(gcd(a, b)) 是 (x) 的倍数,当且仅当 (a, b) 都是 (x) 的倍数。因此在操作 3 的结果集合中 (x) 的倍数的出现次数,就是两个被操作集合中 (x) 的倍数的出现次数的乘积。于是操作 3 可以转化为将两个 ( exttt{bitset})((g))做 (operatorname{and}),这部分时间复杂度 (mathcal{O}(frac{d}{omega}))。
但是我们还需要从 (f) 推出 (g),从 (g) 还原回 (f),这两个过程都是 (mathcal{O}(dlog d)) 的,太慢了。考虑不维护 (f),直接对 (g) 进行运算,并通过 (g) 回答询问。
操作 2 对 (g) 的影响同样是把两个 ( exttt{bitset}) 做 (operatorname{xor})。
考虑操作 1。可以对每个数值 (v),预处理出集合 ({v}) 所对应的 (g)((g(x) = [x|v]))。则操作 1 就是将一个 ( exttt{bitset}) 赋值为另一个,时间复杂度 (mathcal{O}(frac{d}{omega}))。
考虑回答询问:已知所有数值 (x) 的倍数的出现次数,求给定的数值 (v) 的出现次数。用莫比乌斯反演:(g(x) = sum_{x | y}f(y)Leftrightarrow f(x) = sum_{x | y} mu(frac{y}{x})g(y))。也就是对 (v) 的所有倍数,乘以一个系数((mu(frac{y}{v})))后相加。可以对所有数值 (v),预处理一个 ( exttt{bitset}):(mathrm{coef}_v),表示求答案时每个数字对应的系数。则:(mathrm{ans}(i, v) = mathrm{bitcnt}(g_ioperatorname{and} mathrm{coef}_v)mod 2)。
总时间复杂度 (mathcal{O}(dlog d + q cdotfrac{d}{omega}))。空间复杂度 (mathcal{O}((n + d)frac{d}{omega}))。
参考代码
实际提交时建议使用读入、输出优化,详见本博客公告。
// problem: CF1097F
#include <bits/stdc++.h>
using namespace std;
#define mk make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
const int MAXN = 1e5, MAXD = 7000;
int n, q;
int p[MAXD + 5], cnt_p, mu[MAXD + 5];
bool v[MAXD + 5];
bitset<MAXD + 5> st[MAXD + 5], coef[MAXD + 5], g[MAXN + 5];
void init(int d) {
mu[1] = 1;
for (int i = 2; i <= d; ++i) {
if (!v[i]) {
p[++cnt_p] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt_p && p[j] * i <= d; ++j) {
v[p[j] * i] = 1;
if (i % p[j] == 0)
break;
mu[p[j] * i] = -mu[i];
}
}
for (int i = 1; i <= d; ++i) {
for (int j = i; j <= d; j += i) {
st[j][i] = 1;
coef[i][j] = (mu[j / i] + 2) % 2;
}
}
}
int main() {
init(MAXD);
cin >> n >> q;
while (q--) {
int op, x, y, z;
cin >> op;
if (op == 1) {
cin >> x >> y;
g[x] = st[y];
} else if (op == 2) {
cin >> x >> y >> z;
g[x] = (g[y] ^ g[z]);
} else if (op == 3) {
cin >> x >> y >> z;
g[x] = (g[y] & g[z]);
} else {
cin >> x >> y;
int ans = (g[x] & coef[y]).count() % 2;
cout << ans;
}
}
return 0;
}