题目描述
一道非常平常的主席树题,其实还是蛮经典的
给出一个森林,满足两种操作
-
求一条路径上的第 (k) 大值
-
链接两个树
强制在线
问题解决
看到第 (k) 小,自然而然就想到了主席树,但是要求不断连边,所以考虑 (LCT) 但是时间复杂度和代码度都不允许,区间第 (k) 小好像没什么思虑去优化,所以考虑来优化连边, (n) 颗树的连边貌似只有启发式合并了,但是这里要去思考如何维护主席树上的值,而且要满足时间复杂度
启发式合并的时候,我们把小的合并到大的上面去,合并的时候,我们要暴力把要修改的节点的父节点来修改这个节点,然后更新对应的 (st)表
查询的时候我们有一点书上差分的感觉,因为主席树本质上和差分有点像,在树上建主席树,记录下(x,y,lca(x,y),fa(lca(x,y))) 的值,查询的时候遵循树上差分的想法,来查询就好了
总时间复杂度是 (O(nlog^2n)),但还是要卡一下时间
代码实现
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=8e4+5,maxe=4*maxn;
int lnk[maxn],son[maxe],nxt[maxe],cnt,tot;
int T,n,m,q;
int a[maxn],Fa[maxn],Son[maxn],hed[maxn];
inline void add_e(int x,int y){
son[++tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;
}
struct node{
int size,ls,rs;
}t[maxn*600];
int root[maxn];
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='
'||x=='r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
inline int read(){
int x=0;char ch=' ';int f=1;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
void build(int &now,int l,int r){
now=++cnt;t[now].size=0;
if(l==r)return ;
int mid=(r-l>>1)+l;
build(t[now].ls,l,mid);
build(t[now].rs,mid+1,r);
}
void insert(int &now,int pre,int l,int r,int x){
now=++cnt;t[now]=t[pre];t[now].size++;
if(l==r)return ;
int mid=(r-l>>1)+l;
if(x<=mid)insert(t[now].ls,t[pre].ls,l,mid,x);
else insert(t[now].rs,t[pre].rs,mid+1,r,x);
}
int b[maxn],size;
int query(int x,int y,int pre1,int pre2,int l,int r,int k){
if(l==r)return b[l];
int lsize=t[t[x].ls].size+t[t[y].ls].size-t[t[pre1].ls].size-t[t[pre2].ls].size;
int mid=(r-l>>1)+l;
if(k<=lsize)return query(t[x].ls,t[y].ls,t[pre1].ls,t[pre2].ls,l,mid,k);
else return query(t[x].rs,t[y].rs,t[pre1].rs,t[pre2].rs,mid+1,r,k-lsize);
}
inline int Hash(int x){
return lower_bound(b+1,b+1+size,x)-b;
}
int find(int x){
return Fa[x]==x?x:Fa[x]=find(Fa[x]);
}
int st[maxn][17],dep[maxn],vis[maxn];
void dfs(int x,int father,int rt){
st[x][0]=father;
for(int k=1;k<=16;k++){
st[x][k]=st[st[x][k-1]][k-1];
}
Son[rt]++;dep[x]=dep[father]+1;Fa[x]=father;vis[x]=1;
insert(root[x],root[father],1,size,Hash(a[x]));
for(int j=lnk[x];j;j=nxt[j]){
if(son[j]==father)continue;
dfs(son[j],x,rt);
}
}
inline int Lca(int x,int y){
if(x==y)return x;
if(dep[x]>dep[y])swap(x,y);
for(int k=16;k>=0;k--){
if(dep[st[y][k]]>=dep[x])y=st[y][k];
}
if(x==y)return x;
for(int k=16;k>=0;k--)
if(st[x][k]!=st[y][k])x=st[x][k],y=st[y][k];
return st[x][0];
}
int main(){
freopen("P3302.in","r",stdin);
freopen("P3302.out","w",stdout);
T=read();T=1;
while(T--){
memset(lnk,0,sizeof lnk);
memset(dep,0,sizeof dep);
memset(vis,0,sizeof vis);
memset(st,0,sizeof st);
memset(Son,0,sizeof Son);
tot=0;cnt=0;n=read();m=read();q=read();
for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i],Fa[i]=i;
sort(b+1,b+1+n);
size=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=m;i++){
int x=read(),y=read();
add_e(x,y);add_e(y,x);
}
build(root[0],1,size);
for(int i=1;i<=n;i++){
if(!vis[i]){dfs(i,0,i);Fa[i]=i;}
}
int lastans=0;
for(int i=1;i<=q;i++){
char ch[3];
int x,y,k;
scanf("%s",ch);
x=read()^lastans;
y=read()^lastans;
if(ch[0]=='Q'){
k=read()^lastans;
int lca=Lca(x,y);
lastans=query(root[x],root[y],root[lca],root[st[lca][0]],1,size,k);
printf("%d
",lastans);
}
else {
add_e(x,y);add_e(y,x);
int fx=find(x),fy=find(y);
if(son[fx]<son[fy]){
swap(fx,fy);swap(x,y);
}
dfs(y,x,fx);
}
}
}
return 0;
}