传送门
写这题需要点亮的技能有:LCA(倍增orLCT求法),启发式合并,主席树.
先讲暴力的算法,我们可以用并查集维护这个森林,当两个点连通的时候,我们暴力的去遍历其中一棵树,建立新的LCA和主席树两种结构,这个很好搞定.问题是,这样的时间复杂度和空间复杂度都不可承受,下面介绍启发式合并的思想优化这个算法.
启发式合并这个东西,是一个很神奇的思想,因为这是一个森林,而且点集的规模是只增不减的,这类题目我们可以用并查集+启发式合并,优化暴力算法.考虑合并的时候把小的点集合并到大的点集里,那么这个合并复杂度会从暴力的O(N)优化O到(log(N)).具体证明大家可以上网找找,这个复杂度感性上应该很容易接受.
至于合并的时候更新主席树的操作比较简单,倍增LCA也容易,具体就是只拓展点集比较小的那个点重新求一遍倍增数组就好了.
代码:
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#define lson(i) tree[i].l
#define rson(i) tree[i].r
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int maxlen = 25;
inline void read(int &a){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
a = x*f;
}
struct Tree{
int l,r,sum;
}tree[N*180];
int n,m,t,tot,a[N],nxt[N*4],dp[maxlen+1][N],d[N],head[N],to[N*4],ct,fa[N],siz[N],root[N],vis[N];
void addedge(int x,int y){
nxt[++ct] = head[x];head[x] = ct;to[ct] = y;
}
void prework(int s){
queue<int> q;
q.push(s);
d[s] = d[dp[0][s]]+1;
while(q.size()){
int u = q.front();
q.pop();
for(int i = head[u];i;i = nxt[i]){
int y = to[i];
if(y == dp[0][u]) continue;
dp[0][y] = u;
d[y] = d[u] + 1;
fir(k,1,maxlen)
dp[k][y] = dp[k-1][dp[k-1][y]];
q.push(y);
}
}
}
int lca(int x,int y){
if(d[x] > d[y]) return lca(y,x);
afir(i,maxlen,0)
if(d[dp[i][y]] >= d[x]) y = dp[i][y];
if(x == y) return x;
afir(i,maxlen,0)
if(dp[i][x] != dp[i][y]) x = dp[i][x],y = dp[i][y];
return dp[0][x];
}
int find(int x){
int r = x,i=x,j=x;while(fa[r] != r) r = fa[r];while(i!=fa[i]) j = fa[i],fa[i] = r,i = j;return r;
}
void add(int pre,int &now,int l,int r,int p){
now = ++tot;
tree[now] = tree[pre];
tree[now].sum ++;
if(l >= r) return;
int mid = l + r >> 1;
if(p <= mid) add(lson(pre),lson(now),l,mid,p);
else add(rson(pre),rson(now),mid+1,r,p);
}
int query(int l,int r,int p,int q,int gra,int fa,int k){
if(l >= r) return l;
int mid = l + r >> 1;
int sum = tree[lson(q)].sum + tree[lson(p)].sum - tree[lson(gra)].sum - tree[lson(fa)].sum;
if(k <= sum) return query(l,mid,lson(p),lson(q),lson(gra),lson(fa),k);
else return query(mid+1,r,rson(p),rson(q),rson(gra),rson(fa),k-sum);
}
void dfs(int u){
add(root[dp[0][u]],root[u],0,1e9+10,a[u]);
for(int i = head[u];i;i=nxt[i]){
int y = to[i];
if(y == dp[0][u]) continue;
dfs(y);
}
}
void unit(int x,int y){
int fx = find(x),fy = find(y);
siz[fy] += siz[fx];
siz[fx] = 0;
fa[fx] = fy;
dp[0][x] = y;
d[x] = d[y]+1;
fir(k,1,maxlen)
dp[k][x] = dp[k-1][dp[k-1][x]];
prework(x);
dfs(x);
}
int main(){
int test;
read(test);read(n);read(m);read(t);
fir(i,1,n){
read(a[i]),fa[i] = i,siz[i] = 1;
add(root[0],root[i],0,1e9+10,a[i]);
d[i] = 1;
}
fir(i,1,m){
int x,y;
read(x);read(y);
int fx = find(x),fy = find(y);
addedge(x,y);addedge(y,x);
if(fx == fy) continue;
if(siz[fx] <= siz[fy]) unit(x,y);
else unit(y,x);
}
int lastans = 0;
fir(i,1,t){
char op;
int x,y,z;
while(op = getchar()) if(isalpha(op)) break;
if(op == 'Q'){
read(x);read(y);read(z);
x = x^lastans,y = y^lastans,z = z^lastans;
printf("%d\n",lastans = query(0,1e9+10,root[x],root[y],root[lca(x,y)],root[dp[0][lca(x,y)]],z));
}
else{
read(x);read(y);
x = x^lastans,y = y^lastans;
int fx = find(x),fy = find(y);
addedge(x,y);addedge(y,x);
if(fx == fy) continue;
if(siz[fx] <= siz[fy]) unit(x,y);
else unit(y,x);
}
}
return 0;
}