题目传送门
sol:假如把这题树上这个条件改成序列,那么对于操作2,求某区间$[l, r]$所有连续子序列的异或和。观察得出若$r - l + 1$是偶数,则答案是$[l, r]$区间的异或和。否则,答案是$[l, r]$区间内下标奇偶性与$l$不同的元素的异或和。那么可以用三颗线段树来分别维护奇数下标、偶数下标、所有下标的异或和。至于在树上完成操作,树剖解决。
- 树剖 + 线段树
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 2e5 + 10; inline int read() { int n = 0; char c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { n = 10 * n + (c ^ '0'); c = getchar(); } return n; } struct {int v, n;} edge[2 * MAXN]; int val[MAXN], head[MAXN]; void add_edge(int u, int v) { static int cnt = 1; edge[cnt].v = v; edge[cnt].n = head[u]; head[u] = cnt++; } // 树剖 int fat[MAXN], son[MAXN], siz[MAXN], dep[MAXN]; int top[MAXN], rnk[MAXN], ord[MAXN]; void dfs(int u, int f, int d) { fat[u] = f, dep[u] = d, siz[u] = 1; for (int i = head[u]; i != -1; i = edge[i].n) { int v = edge[i].v; if (v == fat[u]) continue; dfs(v, u, d + 1); siz[u] += siz[v]; if (siz[v] > siz[son[u]]) { son[u] = v; } } } void dfs(int u, int t) { static int cnt = 1; top[u] = t, ord[u] = cnt, rnk[cnt++] = u; if (!son[u]) return; dfs(son[u], t); for (int i = head[u]; i != -1; i = edge[i].n) { int v = edge[i].v; if (v == fat[u] || v == son[u]) continue; dfs(v, v); } } // 线段树 struct SegTree { struct Node { int l, r; int val; } node[4 * MAXN]; void build(int i, int l, int r) { node[i].l = l, node[i].r = r; node[i].val = 0; if (l == r) return; int mid = l + r >> 1; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); } void update(int i, int ind, int val) { if (node[i].l == node[i].r) { node[i].val = val; return; } int mid = node[i].l + node[i].r >> 1; if (ind <= mid) update(i << 1, ind, val); else update(i << 1 | 1, ind, val); node[i].val = node[i << 1].val ^ node[i << 1 | 1].val; } int query(int i, int l, int r) { if (node[i].l == l && node[i].r == r) return node[i].val; int mid = node[i].l + node[i].r >> 1; if (r <= mid) return query(i << 1, l, r); if (l > mid) return query(i << 1 | 1, l, r); return query(i << 1, l, mid) ^ query(i << 1 | 1, mid + 1, r); } } tree[3]; // 求路径和 int query(int u, int v) { int val = 0, tag; int tu = top[u], tv = top[v]; if ((dep[u] & 1) != (dep[v] & 1)) tag = 2; else tag = !(dep[u] & 1); while (tu != tv) { if (dep[tu] >= dep[tv]) { // 跳tu到u val ^= tree[tag].query(1, ord[tu], ord[u]); u = fat[tu], tu = top[u]; } else { val ^= tree[tag].query(1, ord[tv], ord[v]); v = fat[tv], tv = top[v]; } } if (dep[u] <= dep[v]) { val ^= tree[tag].query(1, ord[u], ord[v]); } else { val ^= tree[tag].query(1, ord[v], ord[u]); } return val; } // 主函数 int main() { int n = read(), m = read(); for (int i = 1; i <= n; i++) val[i] = read(); memset(head, -1, sizeof(head)); for (int i = 2; i <= n; i++) { int u = read(), v = read(); add_edge(u, v); add_edge(v, u); } dfs(1, -1, 1); dfs(1, 1); tree[0].build(1, 1, n), tree[1].build(1, 1, n), tree[2].build(1, 1, n); for (int i = 1; i <= n; i++) { tree[dep[i] & 1].update(1, ord[i], val[i]); tree[2].update(1, ord[i], val[i]); } for (int i = 1; i <= m; i++) { int op = read(); if (op == 1) { int ind = read(), val = read(); tree[dep[ind] & 1].update(1, ord[ind], val); tree[2].update(1, ord[ind], val); } else { int u = read(), v = read(); printf("%d ", query(u, v)); } } return 0; }
因为这道题专门去学了树剖,这题的数据真的是很玄学啊。我一样的代码第一次提交过80%数据超时,之后提交就能AC。另外一份感觉复杂度一样的代码一直过80%数据超时。最后20%数据有毒。