题目描述
现在有一颗以 1 为根节点的由 n 个节点组成的树,树上每个节点上都有一个权值 vi 。现在有 Q 次操作,操作如下:
- 1 x y :查询节点x 的子树中与y 异或结果的最大值
- 2 x y z :查询路径x 到y 上点与z 异或结果最大值
对于异或最大值问题,考虑使用字典树解决。
写了一个非正解的方法,比较愚蠢,线段树套字典树+树链剖分。最早我听说这个方法是错的,十分不解。写完后我才知道问题所在。
呵,出题人你就不能给个512MB吗???(而且这样做,时间复杂度也挺大,线段树套trie的运算量可不是吃素的)
#include <cstdio> #define N 100010 inline char gc() { static char now[1<<16], *S, *T; if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;} return *S++; } inline int read() { int x = 0; char c = gc(); while(c < '0' || c > '9') c = gc(); while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();} return x; } inline void print(int x) { if(!x) {puts("0"); return ;} int dgt[20], now = 0; while(x) {dgt[++now] = x % 10; x/= 10;} for(; now; --now) putchar('0' + dgt[now]); puts(""); } struct edge {int to, next;}e[N<<1]; int val[N], head[N], fa[N], dep[N], sz[N], son[N], st[N], ed[N], rid[N], tp[N]; int n, q, cnt = 1, dfn = 0, ans; inline void ins(int x, int y) {e[++cnt].to = y; e[cnt].next = head[x]; head[x] = cnt;} void dfs1(int x, int f, int d) { fa[x] = f; dep[x] = d; sz[x] = 1; son[x] = 0; for(int i = head[x]; i; i = e[i].next) { int y = e[i].to; if(y != f) { dfs1(y, x, d + 1); sz[x]+= sz[y]; if(!son[x] || sz[y] > sz[son[x]]) son[x] = y; } } } void dfs2(int x, int f) { st[x] = ed[x] = ++dfn; rid[dfn] = x; tp[x] = f; if(!son[x]) return ; dfs2(son[x], f); for(int i = head[x]; i; i = e[i].next) { int y = e[i].to; if(y != fa[x] && y != son[x]) dfs2(y, y); }ed[x] = dfn; } struct node {int next[2];}trie[N*500]; int len = N<<2; inline void add(int p, int x) { for(int i = 30; i >= 0; --i) { int now = (x & (1<<i))?1:0; if(trie[p].next[now]) p = trie[p].next[now]; else p = (trie[p].next[now] = ++len); } } inline int mymax(int x, int y) {return (x>y)?x:y;} inline void query(int p, int x) { int now = 0; for(int i = 30; i >= 0; --i) { int need = (x & (1<<i))?0:1; if(trie[p].next[need]) p = trie[p].next[need], now+= (1<<i); else p = trie[p].next[1 - need]; } ans = mymax(ans, now); } void build(int p, int l, int r) { if(l == r) {add(p, val[rid[l]]); return ;} int mid = (l + r)>>1; build(p<<1, l, mid); build(p<<1|1, mid + 1, r); for(int i = l; i <= r; ++i) add(p, val[rid[i]]); } void getans(int p, int l, int r, int x, int y, int z) { if(x <= l && r <= y) {query(p, z); return ;} int mid = (l + r)>>1; if(x <= mid) getans(p<<1, l, mid, x, y, z); if(mid + 1 <= y) getans(p<<1|1, mid + 1, r, x, y, z); } inline int findans(int x, int y, int z) { int fx = tp[x], fy = tp[y]; while(fx != fy) { if(dep[fx] < dep[fy]) {int temp = fx; fx = fy; fy = temp; temp = x; x = y; y = temp;} getans(1, 1, n, st[fx], st[x], z); x = fa[fx]; fx = tp[x]; } (dep[x] > dep[y])?getans(1, 1, n, st[y], st[x], z):getans(1, 1, n, st[x], st[y], z); } int main() { n = read(); q = read(); for(int i = 1; i <= n; ++i) val[i] = read(); for(int i = 1; i < n; ++i) {int x = read(), y = read(); ins(x, y); ins(y, x);} dfs1(1, 0, 1); dfs2(1, 1); build(1, 1, n); for(int i = 1; i <= n; ++i) { int opt = read(), x = read(), y = read(); ans = 0; if(opt == 1) { getans(1, 1, n, st[x], ed[x], y); print(ans); }else { int z = read(); findans(x, y, z); print(ans); } } return 0; }
非要逼着写正解。
对于子树,就使用dfs序转为序列可持久化字典树求解。对于路径,拆成lca引导的两条路径,使用可持久化字典树进行维护。
至今不知道大佬们怎么做到只维护一棵字典树。
#include <cstdio> #define N 100010 inline char gc() { static char now[1<<16], *S, *T; if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;} return *S++; } inline int read() { int x = 0; char c = gc(); while(c < '0' || c > '9') c = gc(); while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();} return x; } inline void print(int x) { if(!x) {puts("0"); return ;} int dgt[20], now = 0; while(x) {dgt[++now] = x % 10; x/= 10;} for(; now; --now) putchar('0' + dgt[now]); puts(""); } struct edge {int to, next;}e[N<<1]; int val[N], head[N], st[N], ed[N], rid[N]; int n, q, cnt = 1, dfn = 0; inline void ins(int x, int y) {e[++cnt].to = y; e[cnt].next = head[x]; head[x] = cnt;} inline int mymax(int x, int y) {return (x > y)?x:y;} int ans, each; int root1[N], L1[N*50], R1[N*50], v1[N*50], len1 = 0; void add1(int pre, int &now, int key, int dgt) { now = ++len1; v1[now] = v1[pre] + 1; if(dgt < 0) return ; int x = key & (1<<dgt); if(!x) {R1[now] = R1[pre]; add1(L1[pre], L1[now], key, dgt - 1);} else {L1[now] = L1[pre]; add1(R1[pre], R1[now], key, dgt - 1);} } void query1(int pre, int now, int key, int dgt) { if(dgt < 0) {ans = mymax(ans, each); return ;} int x = key & (1<<dgt), k; if(x) { k = v1[L1[now]] - v1[L1[pre]]; if(k) each|= 1<<dgt, query1(L1[pre], L1[now], key, dgt - 1); else query1(R1[pre], R1[now], key, dgt - 1); }else { k = v1[R1[now]] - v1[R1[pre]]; if(k) each|= 1<<dgt, query1(R1[pre], R1[now], key, dgt - 1); else query1(L1[pre], L1[now], key, dgt - 1); } } int root2[N], L2[N*50], R2[N*50], v2[N*50], len2 = 0; void add2(int pre, int &now, int key, int dgt) { now = ++len2; v2[now] = v2[pre] + 1; if(dgt < 0) return ; int x = key & (1<<dgt); if(!x) {R2[now] = R2[pre]; add2(L2[pre], L2[now], key, dgt - 1);} else {L2[now] = L2[pre]; add2(R2[pre], R2[now], key, dgt - 1);} } void query2(int pre, int now, int key, int dgt) { if(dgt < 0) {ans = mymax(ans, each); return ;} int x = key & (1<<dgt), k; if(x) { k = v2[L2[now]] - v2[L2[pre]]; if(k) each|= 1<<dgt, query2(L2[pre], L2[now], key, dgt - 1); else query2(R2[pre], R2[now], key, dgt - 1); }else { k = v2[R2[now]] - v2[R2[pre]]; if(k) each|= 1<<dgt, query2(R2[pre], R2[now], key, dgt - 1); else query2(L2[pre], L2[now], key, dgt - 1); } } int f[N][17], dep[N]; void dfs(int x, int fa) { f[x][0] = fa; st[x] = ++dfn; rid[dfn] = x; add2(root2[fa], root2[x], val[x], 30); for(int i = head[x]; i; i = e[i].next) { int y = e[i].to; if(y != fa) {dep[y] = dep[x] + 1; dfs(y, x);} }ed[x] = dfn; } inline int lca(int x, int y) { if(dep[x] > dep[y]) {int temp = x; x = y; y = temp;} for(int i = 16; i >= 0; --i) if(dep[y] - dep[x] >= (1<<i)) y = f[y][i]; if(x == y) return x; for(int i = 16; i >= 0; --i) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } int main() { n = read(); q = read(); for(int i = 1; i <= n; ++i) val[i] = read(); for(int i = 1; i < n; ++i) {int x = read(), y = read(); ins(x, y); ins(y, x);} dep[1] = 1; dfs(1, 0); for(int j = 1; j < 17; ++j) for(int i = 1; i <= n; ++i) f[i][j] = f[f[i][j - 1]][j - 1]; for(int i = 1; i <= n; ++i) add1(root1[i - 1], root1[i], val[rid[i]], 30); for(int i = 1; i <= q; ++i) { int opt = read(), x = read(), y = read(); ans = each = 0; if(opt == 1) {query1(root1[st[x] - 1], root1[ed[x]], y, 30); print(ans);} else { int z = read(), w = lca(x, y); int pre = f[w][0]; query2(root2[pre], root2[x], z, 30); each = 0; query2(root2[pre], root2[y], z, 30); print(ans); } } return 0; }
呼,现在就舒坦多了。