P3224 [HNOI2012]永无乡
题目描述
永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以 到达岛 b ,则称岛 a 和岛 b 是连通的。
现在有两种操作:
B x y 表示在岛 x 与岛 y 之间修建一座新桥。
Q x k 表示询问当前与岛 x 连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪座,请你输出那个岛的编号。
输入输出格式
输入格式:第一行是用空格隔开的两个正整数 n 和 m ,分别表示岛的个数以及一开始存在的桥数。
接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi ,表示一开始就存在一座连接岛 ai和岛 bi的桥。
后面剩下的部分描述操作,该部分的第一行是一个正整数 q ,表示一共有 q 个操作,接下来的 q行依次描述每个操作,操作的 格式如上所述,以大写字母 Q 或 B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。
输出格式:对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 −1 。
输入输出样例
5 1
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
-1
2
5
1
2
说明
对于 20% 的数据 n≤1000,q≤1000
对于 100% 的数据 n≤100000,m≤n,q≤300000
Solution:
本题操作看完就是一眼题,Spaly码农上线。
初始时每个节点为一棵splay,然后对于两点的联系,由于各树高均为$\log n$级别,所以直接暴力往上跳找到这两点所在的splay的根节点,若不同就启发式合并,把节点数较少的splay暴力插入到节点数多的那棵splay中。至于查询就是裸的第k大值查询了。
时间复杂度$O(n\log n)$。
代码:
/*Code by 520 -- 9.20*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) #define son(x) (x==ch[fa[x]][1]) using namespace std; const int N=500005; int n,m,Q[N],tot; int root[N],cnt,ch[N][2],fa[N],date[N],id[N],siz[N]; int gi(){ int a=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar(); return a; } il int newnode(int x){date[++cnt]=x;siz[cnt]=1;return cnt;} il void pushup(int rt){siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1;} il void rotate(int x){ int y=fa[x],z=fa[y],b=son(x),c=son(y),a=ch[x][!b]; if(z) ch[z][c]=x; fa[x]=z; if(a) fa[a]=y; ch[y][b]=a; fa[y]=x,ch[x][!b]=y; pushup(y),pushup(x); } il void splay(int x,int i){ while(fa[x]!=i) { int y=fa[x],z=fa[y]; if(z==i) rotate(x),fa[x]=i; else { if(son(x)==son(y)) rotate(y),rotate(x); else rotate(x),rotate(x); } } } int getfa(int x){return !fa[x]?x:getfa(fa[x]);} void insert(int &rt,int x){ if(!rt) {rt=x,siz[x]=1;return;} if(date[x]<date[rt]) insert(ch[rt][0],x),fa[ch[rt][0]]=rt; else insert(ch[rt][1],x),fa[ch[rt][1]]=rt; pushup(rt); } int getrank(int rt,int k){ if(siz[rt]<k) return -1; if(siz[ch[rt][0]]+1==k) return rt; if(siz[ch[rt][0]]+1>k) return getrank(ch[rt][0],k); else return getrank(ch[rt][1],k-siz[ch[rt][0]]-1); } void dfs(int x){ if(!x) return ; dfs(ch[x][0]),Q[++tot]=x,dfs(ch[x][1]); } il void merge(int x,int y){ x=getfa(x),y=getfa(y),tot=0; if(siz[x]>=siz[y]){ dfs(y);For(i,1,tot) insert(x,Q[i]),splay(Q[i],0),x=Q[i]; } else { dfs(x);For(i,1,tot) insert(y,Q[i]),splay(Q[i],0),y=Q[i]; } } int main(){ n=gi(),m=gi(); int x,y;char s[3]; For(i,1,n) x=gi(),root[i]=newnode(x); while(m--){ x=gi(),y=gi(); merge(x,y); } m=gi(); while(m--){ scanf("%s",s),x=gi(),y=gi(); if(s[0]=='Q') printf("%d\n",getrank(getfa(x),y)); else { x=getfa(x),y=getfa(y); if(x!=y) merge(x,y); } } return 0; }