版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/84573335
题目链接
题意:
给你一个森林,点有点权,有两种操作,一种是把x与y之间连一条边,保证连边之后仍然是树形结构;一种是询问x到y的路径上第k大的点权是多少,保证x到y连通并且路径上有第k大的点。n<=80000
题解;
如果没有连边,那么就是count on tree那道题,可以在这里。
那么我们就考虑连边怎么处理。常见的支持连边的东西有并查集、LCT和启发式合并以及暴力合并。如果暴力合并肯定复杂度不对,这题据说是可以LCT的,但是写起来感觉会比较麻烦,我用并查集和启发式合并来维护连边操作。
具体地我们对于每次连边,我们可以用并查集维护每个点的连通情况,并且顺便记录一下每个连通块的大小。我们根据连通块的大小,进行启发式合并,我们比较暴力地重新对比较小的那棵主席树进出重构,重构的过程还有重新求一遍倍增数组和每个点在树中的深度。具体看看代码实现吧,没有其他的思维难度了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,T,hed[500010],cnt,fa[500010],f[200010][21],b[200010],c[200010],ji,sz[200010];
int s[20000100],root[2000010],ans,vis[2000010],dep[2000010],ls[20000100],rs[20000100],cnt1;
struct node
{
int to,next;
}a[800010];
char opt;
inline void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=hed[from];
hed[from]=cnt;
}
inline int getr(int x)
{
if(x==fa[x])
return x;
fa[x]=getr(fa[x]);
return fa[x];
}
inline void update(int &rt,int l,int r,int x)
{
int mid=(l+r)>>1;
s[++cnt1]=s[rt]+1;
ls[cnt1]=ls[rt];
rs[cnt1]=rs[rt];
rt=cnt1;
if(l==r)
return;
if(x<=mid)
update(ls[rt],l,mid,x);
else
update(rs[rt],mid+1,r,x);
}
inline void dfs(int x)
{
vis[x]=1;
dep[x]=dep[f[x][0]]+1;
root[x]=root[f[x][0]];
update(root[x],1,ji,b[x]);
for(int i=1;i<=20;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y==f[x][0])
continue;
int fx=getr(x),fy=getr(y);
fa[fy]=fx;
sz[fx]+=sz[fy];
f[y][0]=x;
dfs(y);
}
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=20;i>=0;--i)
{
if(dep[x]-dep[y]>=(1<<i))
x=f[x][i];
}
if(x==y)
return x;
for(int i=20;i>=0;--i)
{
if(f[x][i]!=f[y][i]&&dep[f[x][i]])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
inline int query(int x,int y,int xx,int yy,int l,int r,int k)
{
if(l==r)
return l;
int mid=(l+r)>>1;
int gg=s[ls[x]]+s[ls[y]]-s[ls[xx]]-s[ls[yy]];
if(gg>=k)
return query(ls[x],ls[y],ls[xx],ls[yy],l,mid,k);
else
return query(rs[x],rs[y],rs[xx],rs[yy],mid+1,r,k-gg);
}
inline void insert(int x,int y)
{
dep[x]=dep[f[x][0]]+1;
root[x]=root[f[x][0]];
update(root[x],1,ji,b[x]);
for(int i=1;i<=20;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y==f[x][0])
continue;
f[y][0]=x;
insert(y,x);
}
}
int main()
{
scanf("%d",&n);
scanf("%d%d%d",&n,&m,&T);
for(int i=1;i<=n;++i)
{
scanf("%d",&b[i]);
c[i]=b[i];
}
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
sort(c+1,c+n+1);
ji=unique(c+1,c+n+1)-c-1;
for(int i=1;i<=n;++i)
b[i]=lower_bound(c+1,c+ji+1,b[i])-c;
for(int i=1;i<=n;++i)
{
fa[i]=i;
sz[i]=1;
}
for(int i=1;i<=n;++i)
{
if(!vis[i])
dfs(i);
}
for(int i=1;i<=T;++i)
{
opt=getchar();
while(opt!='Q'&&opt!='L')
opt=getchar();
int x,y,z;
if(opt=='Q')
{
scanf("%d%d%d",&x,&y,&z);
x^=ans;
y^=ans;
z^=ans;
int xx=lca(x,y),yy=f[xx][0];
ans=query(root[x],root[y],root[xx],root[yy],1,ji,z);
ans=c[ans];
printf("%d\n",ans);
}
else
{
scanf("%d%d",&x,&y);
x^=ans;
y^=ans;
int fx=getr(x),fy=getr(y);
if(sz[fx]>sz[fy])
{
swap(x,y);
swap(fx,fy);
}
f[x][0]=y;
insert(x,y);
add(x,y);
add(y,x);
fa[fx]=fy;
sz[fy]+=sz[fx];
}
}
return 0;
}