题意:给定一个森林,每个点有权值,有2个操作,一个是求x到y路径上第k小权值是什么,此时保证xy联通且存在k小权值,第二个操作是连接x到y这条边,操作完成后还是个森林.
Input
第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。第三行包含N个非负整数表示 N个节点上的权值。
接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分。
Output
对于每一个第一类操作,输出一个非负整数表示答案。
第一个操作,传统的做法,我们对于每个点建立一个可持久化线段树,代表根到这个点路径上权值的信息,在查询x和y的时候,我们求出x和y的最近公共祖先,然后在线段树上x+y-lca-father(lca)的信息就是我们要求的答案。
第二问的话我们直接暴力连边(拥有较多节点的那个点作为父亲),对于小的那个树,直接暴力修改倍增数组,需要注意的是一个点在森林中的深度变化是没有规律的,所以求倍增数组的时候要for满。
代码如下:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100005;
int sum[MAXN * 150], lc[MAXN * 150], rc[MAXN * 150], root[MAXN];
int first[MAXN], next[MAXN << 1], go[MAXN << 1], t, sb;
int n, m, i, j, k, q[MAXN], x, y, z, T, l, r, inf;
int f[MAXN][17], ans, dep[MAXN], a[MAXN], len, g[MAXN];
int fa[MAXN], size[MAXN];
char c[10];
inline int get()
{
char c;
while ((c = getchar()) < 48 || c > 57);
int res = c - 48;
while ((c = getchar()) >= 48 && c <= 57)
res = res * 10 + c - 48;
return res;
}
inline int find(int x)
{
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
inline void add(const int &x, const int &y)
{
next[++t] = first[x]; first[x] = t; go[t] = y;
next[++t] = first[y]; first[y] = t; go[t] = x;
}
inline int getlca(int x, int y)
{
if (dep[x] < dep[y]) swap(x, y);
for(int i = g[dep[x] - dep[y]]; i >= 0 && f[x][0]; i --)
{
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
}
for(int i = g[dep[y]]; i >= 0 && f[x][0] != f[y][0]; i --)
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
inline void insert(int &x, int y, int p, int q, int w)
{
x = ++len;
sum[x] = sum[y] + 1;
if (p == q) return;
lc[x] = lc[y];
rc[x] = rc[y];
int mid = (p + q) >> 1;
if (mid >= w) insert(lc[x], lc[y], p, mid, w);
else insert(rc[x], rc[y], mid + 1, q, w);
}
inline int find(int x, int y, int z, int zz, int k, int p, int q)
{
if (p == q) return p;
int tt = sum[lc[x]] + sum[lc[y]] - sum[lc[z]] - sum[lc[zz]];
int mid = (p + q) >> 1;
if (tt >= k) return find(lc[x], lc[y], lc[z], lc[zz], k, p, mid);
else return find(rc[x], rc[y], rc[z], rc[zz], k - tt, mid + 1, q);
}
inline void dfs(int now, int fat)
{
dep[now] = dep[fat] + 1;
for(int i = 1; i <= 16; i ++)
f[now][i] = f[f[now][i - 1]][i - 1];
insert(root[now], root[fat], 0, inf, a[now]);
for(int i = first[now]; i; i = next[i])
if (go[i] != fat)
{
f[go[i]][0] = now;
dfs(go[i], now);
}
}
int main()
{
freopen("forest.in", "r", stdin);
freopen("forest.out", "w", stdout);
g[1] = 0;
x = 2;
for(i = 1; i <= 17; i ++)
{
for(j = x; j < (x << 1) && j <= 100000; j ++)
g[j] = i;
x <<= 1;
}
sb = get();
n = get(); m = get(); T = get();
for(i = 1; i <= n; i ++)
a[i] = get(), inf = max(inf, a[i]), fa[i] = i, size[i] = 1;
for(i = 1; i <= m; i ++)
{
x = get(); y = get();
add(x, y);
int xx = find(x), yy = find(y);
if (size[xx] < size[yy]) swap(x, y), swap(xx, yy);
fa[yy] = xx;
size[xx] += size[yy];
}
for(i = 1; i <= n; i ++)
if (!dep[i]) dfs(i, 0);
while (T --)
{
scanf("%s", c);
if (c[0] == 'Q')
{
x = get(); y = get(); k = get();
x ^= ans; y ^= ans; k ^= ans;
int lca = getlca(x, y);
ans = find(root[x], root[y], root[lca], root[f[lca][0]], k, 0, inf);
printf("%d\n", ans);
}
else
{
x = get(); y = get();
x ^= ans; y ^= ans;
int xx = find(x), yy = find(y);
if (size[xx] < size[yy]) swap(xx, yy), swap(x, y);
fa[yy] = xx;
size[xx] += size[yy];
add(x, y);
f[y][0] = x;
dfs(y, x);
}
}
}