题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3123
题目大意:给出一个有n个节点的森林,接下来有m次操作,每次操作有以下两种:
1、Q x y k : 询问节点 x 到节点 y 这条链上的点的第 k 大的权值是多少。(x和y保证在一个联通块内)
2、L x y : 在节点 x 和节点 y 之间连一条边。(x和y保证不在一个联通块内)
同时这些操作都是强制在线的。
题目思路:如果只有第一种操作,由于是强制在线的,我们优先考虑借助主席树和LCA来做,做一遍dfs,每个节点以其父节点为前置节点更新主席树就行,询问的时候就是用主席树查询 。
现在来考虑第二种情况,由于x 和 y不属于同一个联通块,那么这两个点连一条边之后,其所在两个树会合并成一个新的树,对于这一个新的树我们如果重新选择根节点再重新dfs建主席树的话,这个时间复杂度是不合理的。
我们可以考虑用启发式合并的思想对这两个树合并,先考虑两个树的节点个数,将节点个数较少的点(x)接到节点个数较多的那个树(y)下方,更新一下节点个数较少的点的根节点为 x 节点,x节点的父节点自然就为y节点了,再对x节点做一遍dfs,更新一下LCA和主席树的信息。由于一直是小的往大的去合并,所以总的更新次数就会比你随便选择根节点的更新次数要小很多,所以时间复杂度也是合理的。
具体实现看代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
const int MX = 1e5 + 5;
const int N = 4e7;
const int inf = 0x3f3f3f3f;
int n, m, q;
int val[MX];
int sum[N], ls[N], rs[N];
int root[MX], all;
struct edge {int v, nxt;} E[MX << 1];
int head[MX], tot;
int dep[MX], ST[MX][20], sz[MX];
int vis[MX], belong[MX], tree_rt[MX];
void init(int _n) {
for (int i = 1; i <= _n; i++) {
vis[i] = belong[i] = root[i] = dep[i] = 0;
head[i] = -1;
}
tot = all = 0;
}
void add_edge(int u, int v) {
E[tot].v = v; E[tot].nxt = head[u];
head[u] = tot++;
}
void update(int l, int r, int &rt, int pre, int pos, int v) {
rt = ++all;
ls[rt] = ls[pre]; rs[rt] = rs[pre]; sum[rt] = sum[pre] + v;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) update(l, mid, ls[rt], ls[pre], pos, v);
else update(mid + 1, r, rs[rt], rs[pre], pos, v);
}
int query(int l, int r, int u, int v, int lca, int flca, int k) {
if (l == r) return l;
int res = sum[ls[u]] + sum[ls[v]] - sum[ls[lca]] - sum[ls[flca]];
int mid = (l + r) >> 1;
if (res >= k) return query(l, mid, ls[u], ls[v], ls[lca], ls[flca], k);
else return query(mid + 1, r, rs[u], rs[v], rs[lca], rs[flca], k - res);
}
void dfs(int u, int fa, int id) {
vis[u] = 1; belong[u] = id; sz[u] = 1;
for (int i = 1; i < 20; i++)
ST[u][i] = ST[ST[u][i - 1]][i - 1];
update(1, inf, root[u], root[fa], val[u], 1);
for (int i = head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa) continue;
dep[v] = dep[u] + 1;
ST[v][0] = u;
dfs(v, u, id);
sz[u] += sz[v];
}
}
int LCA(int u, int v) {
while (dep[u] != dep[v]) {
if (dep[u] < dep[v]) swap(u, v);
int d = dep[u] - dep[v];
for (int i = 0; i < 20; i++)
if (d >> i & 1) u = ST[u][i];
}
if (u == v) return u;
for (int i = 19; i >= 0; i--) {
if (ST[u][i] != ST[v][i]) {
u = ST[u][i];
v = ST[v][i];
}
}
return ST[u][0];
}
void Merage(int u, int v) {
int rt1 = tree_rt[belong[u]], rt2 = tree_rt[belong[v]];
if (sz[rt1] > sz[rt2]) swap(rt1, rt2), swap(u, v);
add_edge(u, v); add_edge(v, u);
ST[u][0] = v; sz[rt2] += sz[rt1]; dep[u] = dep[v] + 1;
dfs(u, v, belong[v]);
}
int main() {
// FIN;
int T; scanf("%d", &T);
scanf("%d%d%d", &n, &m, &q);
init(n);
for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v); add_edge(v, u);
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
dfs(i, 0, ++cnt);
tree_rt[cnt] = i;
}
}
char op[2];
int u, v, k;
int ans = 0;
while (q--) {
scanf("%s%d%d", op, &u, &v);
u ^= ans; v ^= ans;
if (op[0] == 'Q') {
scanf("%d", &k);
k ^= ans;
int lca = LCA(u, v);
ans = query(1, inf, root[u], root[v], root[lca], root[ST[lca][0]], k);
printf("%d\n", ans);
} else
Merage(u, v);
}
return 0;
}