原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733
题意:
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由桥连接。
现在有两种操作:
B x y 表示在岛 x 与岛 y 之间修建一座新桥。
Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
数据范围
n≤100000,m≤n,q≤300000
题解:
题意即维护连通块,支持查每个连通块的第k小,以及合并连通块。
维护第k小容易想到平衡树。
由于只有n个元素,最多只能合并n-1次,每次遍历较小集合,把元素插到较大集合(启发式合并)的复杂度是O(nlogn)。(启发式合并复杂度证明见这里)
因此,加上平衡树插入的log,最终复杂度O(nlogn^2)。
代码:
#include
#include
#include
#include
#include
using namespace std;
const int N=100005;
int n,m,q;
queue<int> Q;
int val[N],father[N],ch[N][2],fa[N],root[N],size[N];
int getfa(int x)
{
if(father[x]==x) return x;
else return father[x]=getfa(father[x]);
}
void update(int x)
{
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void rotate(int x,int &top)
{
int y=fa[x]; int z=fa[y];
if(y==top) top=x;
else
{if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=z;}
fa[x]=z;
int l=(ch[y][0]==x)? 0:1; int r=l^1;
ch[y][l]=ch[x][r]; fa[ch[x][r]]=y;
ch[x][r]=y; fa[y]=x;
update(y); update(x);
}
void splay(int x,int &top)
{
while(x!=top)
{
int y=fa[x]; int z=fa[y];
if(y!=top)
{
if(ch[y][0]==x^ch[z][0]==y) rotate(x,top);
else rotate(y,top);
}
rotate(x,top);
}
}
void insert(int x,int &y)
{
int tmp=y; int f=0;
while(tmp)
{
if(val[x]0];}
else {f=tmp; tmp=ch[tmp][1];}
}
if(val[x]0]=x; else ch[f][1]=x;
fa[x]=f;
splay(x,y);
}
void merge(int fx,int fy)
{
int x=root[fx]; int y=root[fy];
if(size[x]>size[y]) {swap(x,y);swap(fx,fy);}
father[fx]=fy;
while(!Q.empty()) Q.pop();
Q.push(x);
while(!Q.empty())
{
int u=Q.front(); Q.pop();
if(ch[u][0]) Q.push(ch[u][0]);
if(ch[u][1]) Q.push(ch[u][1]);
ch[u][0]=ch[u][1]=fa[u]=0;
insert(u,root[fy]);
}
}
int find(int nd,int k)
{
int ls=ch[nd][0]; int rs=ch[nd][1];
if(size[nd]-size[rs]==k) return nd;
if(size[ls]>=k) return find(ls,k);
else return find(rs,k-size[nd]+size[rs]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {father[i]=root[i]=i;size[i]=1;}
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int fx,fy; fx=getfa(x); fy=getfa(y);
if(fx==fy) continue;
merge(fx,fy);
}
scanf("%d",&q);
while(q--)
{
char opt[5];
scanf("%s",opt);
if(opt[0]=='B')
{
int x,y;
scanf("%d%d",&x,&y);
int fx,fy;
fx=getfa(x); fy=getfa(y);
if(fx==fy) continue;
merge(fx,fy);
}
else
{
int x,k;
scanf("%d%d",&x,&k);
int fx=getfa(x); int rt=root[fx];
if(size[rt]printf("-1\n");
else printf("%d\n",find(rt,k));
}
}
return 0;
}