首先介绍下树上主席树:
在序列上的主席树,是对序列的每一个前缀都建立一个线段树版本。
而树上主席树,是对树上的每一个节点到根的路径都建立一个线段树版本。
而如果 query(u) 是查询节点 u 到根的路径上的信息,则查询
这样,就可以在权值线段树上二分,查询路径第 k 小值了。
而最后的问题就是加边。可以考虑启发式合并,也就是把节点数少的树合并到节点数多的树上面去,这样每个节点被合并的次数不超过
在合并之后,也必须初始化节点数少的树的倍增LCA数组,以及节点数少的树的线段树版本。所以,一次合并的操作为 O(log2n) 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {int res = 0; bool bo = 0; char c;while (((c = getchar()) < '0' || c > '9') && c != '-');if (c == '-') bo = 1; else res = c - 48;while ((c = getchar()) >= '0' && c <= '9')res = (res << 3) + (res << 1) + (c - 48);return bo ? ~res + 1 : res;
}
inline char get() {char c; while ((c = getchar()) != 'L' && c != 'Q');return c;
}
const int N = 7e5 + 5, Orz = 2e7 + 5, LogN = 20;
struct cyx {int lc, rc, cnt;
} T[Orz];
int n, m, QAQ, rt[N], sze[N], ecnt, nxt[N], adj[N], go[N], fa[N][LogN],
val[N], RT[N], dep[N], lst;
bool vis[N];
void add_edge(int u, int v) {nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void build(int y, int &x, int l, int r, int p) {T[x = ++QAQ] = T[y]; T[x].cnt++;int mid = l + r >> 1; if (l == r) return;if (p <= mid) build(T[y].lc, T[x].lc, l, mid, p);else build(T[y].rc, T[x].rc, mid + 1, r, p);
}
void dfs(int u, int fu, int r) {int i; build(rt[fu], rt[u], 0, 1e9, val[u]); sze[u] = 1; RT[u] = r;dep[u] = dep[fa[u][0] = fu] + 1;for (i = 0; i <= 17; i++) fa[u][i + 1] = fa[fa[u][i]][i];for (int e = adj[u], v; e; e = nxt[e]) {if ((v = go[e]) == fu) continue;dfs(v, u, r); sze[u] += sze[v];}vis[u] = 1;
}
void link(int x, int y) {if (sze[RT[x]] < sze[RT[y]]) swap(x, y);sze[RT[x]] += sze[RT[y]]; dfs(y, x, RT[x]);add_edge(x, y);
}
int lca(int u, int v) {int i; if (dep[u] < dep[v]) swap(u, v);for (i = 18; i >= 0; i--) {if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];if (u == v) return u;}for (i = 18; i >= 0; i--)if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];return fa[u][0];
}
int query(int p1, int p2, int p3, int p4, int l, int r, int k) {if (l == r) return l; int mid = l + r >> 1;int delta = T[T[p1].lc].cnt + T[T[p2].lc].cnt- T[T[p3].lc].cnt - T[T[p4].lc].cnt;if (delta >= k)return query(T[p1].lc, T[p2].lc, T[p3].lc, T[p4].lc, l, mid, k);elsereturn query(T[p1].rc, T[p2].rc, T[p3].rc, T[p4].rc, mid + 1, r, k - delta);
}
int ask(int x, int y, int k) {int z = lca(x, y);return query(rt[x], rt[y], rt[z], rt[fa[z][0]], 0, 1e9, k);
}
int main() {int i, x, y, k; read(); n = read(); m = read(); int Q = read();for (i = 1; i <= n; i++) val[i] = read();for (i = 1; i <= m; i++) x = read(), y = read(), add_edge(x, y);for (i = 1; i <= n; i++) if (!vis[i]) dfs(i, 0, i);while (Q--) {char op = get(); x = read() ^ lst; y = read() ^ lst;if (op == 'L') link(x, y);else k = read() ^ lst, printf("%d\n", lst = ask(x, y, k));}return 0;
}