2733: [HNOI2012]永无乡
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 4444 Solved: 2378
[Submit][Status][Discuss]
Description
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Input
输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000
对于 100%的数据 n≤100000,m≤n,q≤300000
Output
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。
Sample Input
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
Sample Output
-1
2
5
1
2
HINT
Source
看看数据大小估一下复杂度2个log。然后就想到了平衡树+启发式合并,具体实现的话我用了替罪羊树。 为什么要用替罪羊树呢?因为可以进第一页啊
代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
const double alpha=0.75;
inline char tc(void){
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline int read(void){
int a=0;char c;
while((c=tc())<'0'||c>'9');
while(c>='0'&&c<='9')a=a*10+c-'0',c=tc();
return a;
}
inline char gc(void){
char c;
while((c=tc())=='\n'||c=='\r'||c==' ');
return c;
}
int o,n,m,q,f[100001],v[100001],sum[100001],rt[100001],size[100001],ch[100001][2],que[100001],tot,*sgt;
int queo[100001],cnt;
int gf(int x){
return x==f[x]?x:f[x]=gf(f[x]);
}
inline void update(int x){
size[x]=1+size[ch[x][0]]+size[ch[x][1]];
}
inline bool isbad(int x){
if(size[x]*alpha+5<max(size[ch[x][0]],size[ch[x][1]]))return 1;
return 0;
}
void dfs(int x){
if(ch[x][0])dfs(ch[x][0]);
que[++tot]=x;
if(ch[x][1])dfs(ch[x][1]);
}
int build(int l,int r){
if(l>r)return 0;
int mid=l+r>>1,x=que[mid];
size[x]=1,
ch[x][0]=build(l,mid-1),
ch[x][1]=build(mid+1,r);
update(x);
return x;
}
inline void rebuild(int *x){
tot=0,dfs(*x),*x=build(1,tot);
return ;
}
void insert(int &x,int a){
int w=x;
if(x==0){
x=a,ch[a][0]=ch[a][1]=0;
update(a);
return ;
}
if(v[a]<=v[x])insert(ch[x][0],a);
else insert(ch[x][1],a);
update(x);
if(isbad(x))sgt=&x;
return ;
}
void ins(int &x,int a){
sgt=NULL;
insert(x,a);
if(sgt)rebuild(sgt);
}
void dfs1(int x){
if(ch[x][0])dfs(ch[x][0]);
queo[++cnt]=x;
if(ch[x][1])dfs(ch[x][1]);
}
void merge(int a,int b){
a=gf(a),b=gf(b);
if(a==b)return ;
if(sum[a]>sum[b])swap(a,b);
cnt=0,dfs1(rt[a]);
for(int i=1;i<=cnt;++i)ins(rt[b],queo[i]);
rt[a]=0,f[a]=b,sum[b]+=sum[a];
}
int find(int x,int k,int sum){
if(size[x]<k)return -1;
if(k<=size[ch[x][0]])return find(ch[x][0],k,sum+1);
else if(k==size[ch[x][0]]+1)return x;
else return find(ch[x][1],k-size[ch[x][0]]-1,sum+1);
}
int main(void){
register int i,x,y;
n=read(),m=read();
for(i=1;i<=n;++i)v[i]=read(),f[i]=i,sum[i]=1,rt[i]=i,size[i]=1;
for(i=1;i<=m;++i)x=read(),y=read(),merge(x,y);
q=read();
while(q--)
if(gc()=='B')x=read(),y=read(),merge(x,y);
else x=read(),y=read(),printf("%d\n",find(rt[gf(x)],y,0));
return 0;
}