刚刚通过代码我就来写题解了,我是不是很有责任感(其实是被卡的脑瘫了),那么我就来写一下这个简(du)单(liu)的题。
思路
查询路径上的第k大的值,可以用主席树优化,例如对(x,y)路径上(必须联通)查询第k大,可以用一个主席树保存根节点到当前节点x路径上所有节点权值,然后在查询(x,y)时可以用{x,y}的查询区间相加,减去{ lca(x,y),lca(x,y)的父节点 }相加,这样得到的主席树是(x,y)上所有权值的主席树,然后根据判断第k大的方式向下查询
int query(int x,int y,int pre1,int pre2,int l,int r,int k)//x,y是查询路径的两端,pre1是lca(x,y),pre2是lca(x,y)的父节点
{
if(l==r)return b[l];
int lastsize=t[t[x].lc].dat+t[t[y].lc].dat-t[t[pre1].lc].dat-t[t[pre2].lc].dat;
int mid=l+r>>1;
if(k<=lastsize)return query(t[x].lc,t[y].lc,t[pre1].lc,t[pre2].lc,l,mid,k);
return query(t[x].rc,t[y].rc,t[pre1].rc,t[pre2].rc,mid+1,r,k-lastsize);
}
真正让人难以琢磨的是连边,需要我们启发式合并主席树。事实上,就是在连边后,将轻树以连边的一端为起点,连边另一端为起点的父节点,跑一遍dfs。任意一颗线段树,都是根据父节点建立的(每一棵树的根节点的父节点都认为是0),所以只需要再跑dfs时将当前遍历的节点在父节点的基础上建立就好。
void dfs(int now,int father,int rt)
{
Insert(root[now],root[father],1,SIZE,c[a[now]]);//正常的值传递建树就好
for(int i=head[now];i;i=Next[i])
{
int to=ver[i];
if(to==father)continue;
dfs(to,now,rt);
}
}
好了好了,题目讲完了,只要注意到数据的第一个数字时测试数据的编号,就不会像我一样只过了第一个点。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define N 80005
#define lson now<<1
#define ll long long
#define mod 1000000007
#define rep(i,s,t) for(int i=s;i<=t;i++)
//并查集求祖先,st表和dep数组查lca,num通过祖先记录树的轻重
int fa[N]={},st[N][18],num[N],dep[N];
//对a离散化,将建树的值区间压缩为80000,c储存a[i]按照大小排序的序号
int a[N],b[N],SIZE;
map<int,int>c;
//邻接表存图
int tot=0;
int ver[N*4],Next[N*4],head[N*4];
bool vis[N];
//主席树,因为每一次连边都要重新建树,所以要开的很大
int root[N];
struct SegTree{
int dat;
int lc,rc;
}t[N*600];
int cnt=0;
inline int rd(){
int x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
void add(int x,int y)
{
ver[++tot]=y;
Next[tot]=head[x],head[x]=tot;
}
void build(int &now,int l,int r)
{
now=++cnt;
t[now].dat=0;
if(l==r)return ;
int mid=l+r>>1;
build(t[now].lc,l,mid);
build(t[now].rc,mid+1,r);
}
void Insert(int &now,int father,int l,int r,int x)
{
now=++cnt;
t[now]=t[father];
t[now].dat++;;
if(l==r)return ;
int mid=l+r>>1;
if(x<=mid)Insert(t[now].lc,t[father].lc,l,mid,x);
if(x>mid)Insert(t[now].rc,t[father].rc,mid+1,r,x);
}
int query(int x,int y,int pre1,int pre2,int l,int r,int k)
{
if(l==r)return b[l];
int lastsize=t[t[x].lc].dat+t[t[y].lc].dat-t[t[pre1].lc].dat-t[t[pre2].lc].dat;
int mid=l+r>>1;
if(k<=lastsize)return query(t[x].lc,t[y].lc,t[pre1].lc,t[pre2].lc,l,mid,k);
return query(t[x].rc,t[y].rc,t[pre1].rc,t[pre2].rc,mid+1,r,k-lastsize);
}
void dfs(int now,int father,int rt)
{
st[now][0]=father;
rep(i,1,16)st[now][i]=st[st[now][i-1]][i-1];
fa[now]=father;
num[rt]++;
dep[now]=dep[father]+1;
vis[now]=1;
Insert(root[now],root[father],1,SIZE,c[a[now]]);
for(int i=head[now];i;i=Next[i])
{
int to=ver[i];
if(to==father)continue;
dfs(to,now,rt);
}
}
int LCA(int x,int y)
{
if(dep[x]>dep[y])//强制要求x节点是在下方的节点
swap(x,y);//交换,维持性质
for(int k=16;k>=0;k--){
if(dep[st[y][k]]>=dep[x]){
y=st[y][k];
}}
if(x==y)//发现Lca(x,y)=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];//必须返回x的父亲节点,也就是Lca(x,y)
}
int _find(int x){return fa[x]==x?x:_find(fa[x]);}
void solve()
{
int n,m,T,x,y;
cnt=tot=0,c.clear();
memset(head,0,sizeof(head));
memset(head,0,sizeof(st));
n=rd(),m=rd(),T=rd();
rep(i,1,n)
{
a[i]=rd();
b[i]=a[i];
fa[i]=i;
dep[i]=num[i]=0;
vis[i]=0;
}
sort(b+1,b+n+1);
SIZE=unique(b+1,b+1+n)-b-1;
rep(i,1,SIZE)c[b[i]]=i;
dep[0]=0;
build(root[0],1,SIZE);
rep(i,1,m)
{
x=rd(),y=rd();
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++)if(!vis[i])
{
dfs(i,0,i);
fa[i]=i;
}
int lastans=0;
rep(i,1,T)
{
char ch[3];
int x,y,k;
scanf("%s",&ch);
if(ch[0]=='Q')
{
x=rd(),y=rd(),k=rd();
x^=lastans;
y^=lastans;
k^=lastans;
int z=LCA(x,y);
lastans=query(root[x],root[y],root[z],root[st[z][0]],1,SIZE,k);
printf("%d\n",lastans);
}
if(ch[0]=='L')
{
x=rd(),y=rd();
x^=lastans;
y^=lastans;
int u=_find(x);
int v=_find(y);
add(x,y),add(y,x);
if(num[u]>=num[v])
{
dfs(y,x,u);
}
else
{
dfs(x,y,v);
}
}
}
}
int main()
{
int T;
scanf("%d",&T);T=1;
while(T--)
{
solve();
}
}