CF1097F Alex and a TV Show
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\times B = \{ \gcd(a, b)\, \mid\, a \in A,\, b \in B \}\),结果也是一个可重集。
- \(4\ x\ v\)。求第 \(x\) 个可重集里数值 \(v\) 出现的次数在 \(\bmod 2\) 意义下的结果。
\(x, y, z\) 可能相同。
数据范围:\(1\leq n\leq 10^5\),\(1\leq q\leq 10^6\),\(1\leq v\leq 7000\)。
本题题解
设 \(d\) 表示涉及数值的最大值,本题里 \(d\leq 7000\)。
因为只需要求 \(\bmod 2\) 意义下的答案,容易想到用 \(\texttt{bitset}\) 存储每个值出现的次数的奇偶性。具体来说,设 \(f_i(x)\) 表示在第 \(i\) 个可重集里,数值 \(x\) 出现的次数的奇偶性。那么操作 2 就是将两个 \(\texttt{bitset}\) 做 \(\operatorname{xor}\),单次操作的时间复杂度为 \(\mathcal{O}(\frac{d}{\omega})\),其中 \(\omega\) 是位长,可以认为 \(\omega = 64\)。但是操作 3 则难以快速实现。
考虑 FFT 算法的思想,将难以计算的东西转化为“点值”,对点值进行运算,再通过逆操作还原回去。设 \(g_i(x) = (\sum_{x | y} f_i(y))\bmod 2\),也就是【\(x\) 的所有倍数的出现次数之和】的奇偶性。注意到,\(\gcd(a, b)\) 是 \(x\) 的倍数,当且仅当 \(a, b\) 都是 \(x\) 的倍数。因此在操作 3 的结果集合中 \(x\) 的倍数的出现次数,就是两个被操作集合中 \(x\) 的倍数的出现次数的乘积。于是操作 3 可以转化为将两个 \(\texttt{bitset}\)(\(g\))做 \(\operatorname{and}\),这部分时间复杂度 \(\mathcal{O}(\frac{d}{\omega})\)。
但是我们还需要从 \(f\) 推出 \(g\),从 \(g\) 还原回 \(f\),这两个过程都是 \(\mathcal{O}(d\log d)\) 的,太慢了。考虑不维护 \(f\),直接对 \(g\) 进行运算,并通过 \(g\) 回答询问。
操作 2 对 \(g\) 的影响同样是把两个 \(\texttt{bitset}\) 做 \(\operatorname{xor}\)。
考虑操作 1。可以对每个数值 \(v\),预处理出集合 \(\{v\}\) 所对应的 \(g\)(\(g(x) = [x|v]\))。则操作 1 就是将一个 \(\texttt{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\),预处理一个 \(\texttt{bitset}\):\(\mathrm{coef}_v\),表示求答案时每个数字对应的系数。则:\(\mathrm{ans}(i, v) = \mathrm{bitcnt}(g_i\operatorname{and} \mathrm{coef}_v)\bmod 2\)。
总时间复杂度 \(\mathcal{O}(d\log d + q \cdot\frac{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;
}