题解:P10016 [集训队互测 2023] 虹

· · 题解

题意

- `1 l r`:令 $S_0$ 为 $[l,r]$ 的**最小虹**,$\forall u \in S_0$,将 $w_u$ 加 $1$。 - `2 l r u`:求 $\left(\sum_{i=l}^r 19901991^{z_{\gcd(i,u)} w_i} \right) \bmod {20242024}$ 的值。 ### Solution 注意到 $19901991^2 \equiv 1 \pmod{20242024}$。 问题转变为求 $\sum_{i=l}^r [z_{\gcd(i,u)} \bmod 2 = 1 \wedge w_i \bmod 2 = 1]$。 令上述式子的值为 $V$,询问的区间为 $[L,R]$。容易得到答案为 $19901991\times V + R - L + 1 - V$,化简得 $19901990\times V + R - L + 1$。 模 $2$ 意义下的 $+1$ 等价于 $\operatorname{xor} 1$,这启发我们使用 `bitset` 维护。 考虑分块。把操作离线下来,对于修改区间为 $[L,R]$ 的 $1$ 操作: - $L,R$ 不属于同一块: 可以把 $[L,R]$ 拆成 $[L,p],[p + 1,R]$ 两个区间。令 $p$ 为某一块的右端点,把所有 $1$ 操作按 $p$ 排序。 对于每一块,从它的右端点 $p'$ 遍历到 $1$,每次加入一个点。从这个点不断地跳父亲,经过的所有点都标记为 $1$,直到当前跳到的点已经被访问。令该过程的标记数组为 $S$。 在加入点的过程中,所有点的公共祖先深度单调不降。我们维护祖先所在的点编号 $pos$,$T$ 表示祖先到树根简单路径的节点 `bitset`,令 $1$ 表示节点在该路径上,$0$ 反之。则每次加入点后让 $pos$ 一直跳父亲,将经过的点设为 $0$,直到跳到了新的公共祖先。 `S ^ T` 的 `bitset` 集合即为 $[L,p]$ 的最小虹,把这个集合存下来。 对于 $[p + 1,R]$ 的处理同理。 某种实现会带来一个问题:左部分与右部分不相连。这个时候可以把左部分与右部分的 `lca` 和他们公共的 `lca` 挂在节点上,`dfs` 维护一个 `bitset`,到哪个点就处理一下 $S_i$ 即可。 容易证明每个节点只会被访问一次,总复杂度 $O(n\sqrt n)$。 - $L,R$ 属于同一块: 我们没有什么好方法去处理这种情况。题目保证了 $L,R$ 在 $[1,n]$ 中等概率随机,所以期望下 $L,R$ 属于同一块的次数为 $O(\sqrt n)$。 类似第一种,对于 $[L,R]$ 每个点跳父亲,暴力 $O(n)$ 处理这种情况下的 `bitset` 即可。 总复杂度 $O(n\sqrt n)$。 对于每个 $2$ 操作,将他们的 `bitset` 中的 $[L,R]$ 位全部置为 $1$,其他都置为 $0$。 把操作按原顺序拍回去。令 $S_i$ 是上面预处理的 `bitset`。令 `bitset` 类型的 $ans$ 初始全为 $0$。若为 $1$ 操作则令 $ans\gets ans \operatorname{xor} S_i$;若为 $2$ 操作则令 $S_i\gets S_i \operatorname{and} ans$。 对于 $z_i$,我们可以直接爆搜所有质数乘积组合。过程中注意要有 $vis$ 数组,不然复杂度会退化。维护一个 $T$ 表示第 $i$ 位上是奇数还是偶数。搜到 $u$ 直接将对应的询问 $S_i \gets S_i \operatorname{and} T$ 即可。 时间复杂度大约是 $O(n\sqrt n)$,据说是 $O(4\times 10^7)$。 总复杂度 $O(n\sqrt n + \frac{nq}{w})$。 输出时使用 `bitset<N>().count()` 统计即可。 我的代码真的很长,还是打的时候没带脑子了哎。 ```cpp #include <bits/stdc++.h> using namespace std; namespace Fread { const int SIZE = 1 << 21; char buf[SIZE], *S, *T; inline char getchar() { if (S == T) { T = (S = buf) + fread(buf, 1, SIZE, stdin); if (S == T) return '\n'; } return *S++; } } namespace Fwrite { const int SIZE = 1 << 21; char buf[SIZE], *S = buf, *T = buf + SIZE; inline void flush() { fwrite(buf, 1, S - buf, stdout); S = buf; } inline void putchar(char c) { *S++ = c; if (S == T) flush(); } struct POPOSSIBLE { ~POPOSSIBLE() { flush(); } } ztr; } #define getchar Fread::getchar #define putchar Fwrite::putchar namespace Fastio { struct Reader { template <typename T> Reader &operator>>(T &x) { char c = getchar(); T f = 1; while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } x = 0; while (c >= '0' && c <= '9') { x = x * 10 + (c - '0'); c = getchar(); } x *= f; return *this; } Reader &operator>>(char &c) { c = getchar(); while (c == ' ' || c == '\n') c = getchar(); return *this; } Reader() {} } cin; struct Writer { template <typename T> Writer &operator<<(T x) { if (x == 0) { putchar('0'); return *this; } if (x < 0) { putchar('-'); x = -x; } static int sta[45]; int top = 0; while (x) { sta[++top] = x % 10; x /= 10; } while (top) { putchar(sta[top] + '0'); --top; } return *this; } Writer &operator<<(char c) { putchar(c); return *this; } Writer &operator<<(const char *str) { int cur = 0; while (str[cur]) putchar(str[cur++]); return *this; } Writer() {} } cout; } #define endl '\n' #define cin Fastio::cin #define cout Fastio::cout #define de(x) cerr << '[' << #x << ' ' << '=' << ' ' << x << ']' << ' ' #define ed() cerr << endl const int N = 8e4 + 2; const int B = 283; typedef long long ll; constexpr void dd(bitset<N> &x) { for (int i = 0; i <= 10; i++) cerr << x[i]; } int n, q, z[N]; vector<int> g[N]; bitset<N> b[N], S, T; struct node1 { int l, r, id; }; vector<node1> md; struct node2 { int u, id; }; vector<node2> qy; int dfn[N], pos; int f[N][18], fa[N]; void dfs0(int x) { f[dfn[x] = ++pos][0] = fa[x]; for (auto y : g[x]) { if (y == fa[x]) continue; fa[y] = x; dfs0(y); } } int Min(int x, int y) { return dfn[x] < dfn[y] ? x : y; } void init() { dfs0(1); for (int j = 1; j <= __lg(n); j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = Min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]); } int lca(int x, int y) { if (x == y) return x; x = dfn[x]; y = dfn[y]; if (x > y) swap(x, y); ++x; int t = __lg(y - x + 1); return Min(f[x][t], f[y - (1 << t) + 1][t]); } int blo[N]; void init2() { int cnt = 0; for (int l = 1; l <= n; l += B) { ++cnt; int r = min(n, l + B - 1); for (int i = l; i <= r; i++) blo[i] = cnt; } } int st[N][18]; void init3() { for (int i = 1; i <= n; i++) st[i][0] = i; for (int j = 1; j <= __lg(n); j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) st[i][j] = lca(st[i][j - 1], st[i + (1 << j - 1)][j - 1]); } int totlca(int x, int y) { int t = __lg(y - x + 1); return lca(st[x][t], st[y - (1 << t) + 1][t]); } int idx[N]; vector<pair<int, int>> tmp[B + 2]; vector<int> br0[N]; vector<int> prime; vector<bool> isPrime; void LinearSieves(const int n) { isPrime.resize(n + 1, 1); isPrime[0] = isPrime[1] = 0; for (int i = 2; i <= n; i++) { if (isPrime[i]) { prime.emplace_back(i); } for (int &x : prime) { if (i * x > n) break; isPrime[i * x] = 0; if (i % x == 0) { break; } } } } void dfs(int x) { // de(x), dd(S), ed(); T[x] = 1; for (auto &id : br0[x]) b[id] &= S; for (auto &P : prime) { x *= P; if (x > n) break; if (!T[x]) { vector<bool> trp(1, 0); for (int i = P; i <= n; i += P) trp.emplace_back(bool(S[i])), S[i] = z[__gcd(x, i)]; dfs(x); for (int i = P; i <= n; i += P) S[i] = trp[i / P]; } x /= P; } } int len[N]; const int mod = 20242024; vector<int> b0[N], b1[N]; void dfs2(int x) { for (auto id : b1[x]) b[id] |= S; S[x] = 1; for (auto y : g[x]) { if (y == fa[x]) continue; dfs2(y); } S[x] = 0; for (auto id : b0[x]) b[id] ^= S; } signed main() { cin >> n >> q; for (int i = 1; i <= n; i++) cin >> z[i], z[i] &= 1; for (int i = 1, x, y; i < n; i++) { cin >> x >> y; g[x].emplace_back(y); g[y].emplace_back(x); } init(); init2(); for (int i = 1; i <= q; i++) { int op; cin >> op; if (op == 1) { int l, r; cin >> l >> r; md.emplace_back(node1{l, r, i}); if (blo[l] != blo[r]) tmp[blo[r / B * B]].emplace_back(l, i); } else { int l, r, u; cin >> l >> r >> u; len[i] = r - l + 1; if (len[i] >= B) idx[i] = r / B * B; b[i].set(); b[i] >>= N - len[i]; b[i] <<= l; qy.emplace_back(node2{u, i}); } } int bcnt = (n + B - 1) / B; for (int i = 1; i <= bcnt; i++) { sort(tmp[i].begin(), tmp[i].end()); // for (auto x : tmp[i]) // de(i), de(x.first), de(x.second), ed(); } for (int l = 1; l <= n; l += B) { int r = min(n, l + B - 1); auto &h = tmp[blo[r]]; int j = h.size() - 1; if (~j) { auto jmp = [&](int x) { while (x && !S[x]) { S[x] = 1; x = fa[x]; } }; S.reset(); jmp(r); T = S; T[r] = 0; int tag = r; auto clear = [&](int gl) { while (tag != gl) { T[tag] = 0; tag = fa[tag]; } T[tag] = 0; }; while (~j && h[j].first == r) { b[h[j].second] |= S ^ T; --j; } if (!~j) continue; for (int i = r - 1; i >= 1; i--) { jmp(i); clear(lca(tag, i)); while (~j && h[j].first > i) --j; while (~j && h[j].first == i) { b[h[j].second] |= S ^ T; --j; } if (!~j) break; } } } // for (auto &[l, r, id] : md) // dd(b[id]), ed(); for (int i = 1; i <= bcnt; i++) { tmp[i].clear(); } for (auto &[l, r, id] : md) { if (blo[l] != blo[r]) { int R = r / B * B; if (R != r) tmp[blo[R + 1]].emplace_back(r, id); } } for (int i = 1; i <= bcnt; i++) { sort(tmp[i].begin(), tmp[i].end(), greater<pair<int, int>>()); // for (auto x : tmp[i]) // de(i), de(x.first), de(x.second), ed(); } for (int l = 1; l <= n; l += B) { int r = min(n, l + B - 1); auto &h = tmp[blo[r]]; int j = h.size() - 1; if (~j) { auto jmp = [&](int x) { while (x && !S[x]) { S[x] = 1; x = fa[x]; } }; S.reset(); jmp(l); T = S; T[l] = 0; int tag = l; auto clear = [&](int gl) { while (tag != gl) { T[tag] = 0; tag = fa[tag]; } T[tag] = 0; }; while (~j && h[j].first == l) { b[h[j].second] |= S ^ T; --j; } if (!~j) continue; for (int i = l + 1; i <= n; i++) { jmp(i); clear(lca(tag, i)); while (~j && h[j].first < i) --j; while (~j && h[j].first == i) { b[h[j].second] |= S ^ T; --j; } if (!~j) break; } } } init3(); for (auto &[l, r, id] : md) { if (blo[l] == blo[r]) { auto jmp = [&](int x) { while (x && !S[x]) { S[x] = 1; x = fa[x]; } }; S.reset(); jmp(l); T = S; T[l] = 0; int tag = l; auto clear = [&](int gl) { while (tag != gl) { T[tag] = 0; tag = fa[tag]; } T[tag] = 0; }; for (int i = l + 1; i <= r; i++) { jmp(i); clear(lca(tag, i)); } b[id] |= S ^ T; // dd(b[id]), ed(); // dd(S), ed(); // dd(T), ed(); } else { int R = r / B * B; if (R != r) { b1[totlca(l, R)].emplace_back(id); b1[totlca(R + 1, r)].emplace_back(id); b0[totlca(l, r)].emplace_back(id); } } } // for (auto &[l, r, id] : md) // dd(b[id]), ed(); S.reset(); dfs2(1); auto op = md.begin(); S.reset(); for (auto &[u, id] : qy) { while (op != md.end() && (*op).id < id) S ^= b[(*op).id], ++op; b[id] &= S; br0[u].emplace_back(id); } LinearSieves(n); S.reset(); T.reset(); for (int i = 1; i <= n; i++) S[i] = z[1]; for (auto &P : prime) { for (int i = P; i <= n; i += P) S[i] = z[P]; dfs(P); for (int i = P; i <= n; i += P) S[i] = z[1]; } for (auto &id : br0[1]) b[id] &= S; for (auto &[u, id] : qy) { cout << (19901990ll * b[id].count() + len[id]) % mod << endl; } return 0; } ```