[BZOJ] 2733: [HNOI2012]永无乡 #线段树合并+并查集
2733: [HNOI2012]永无乡
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 4123 Solved: 2196
[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
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
2
5
1
2
HINT
Source
Analysis
啊,这题做的真xx艰难
qwq
题目是不难码的,标准的 权值线段树+线段树合并+并查集防重边
线段树合并,前人之述备矣
并查集防重边:合并同一棵线段树,也许会有很大几率(100%-eps)出错;为了安全在合并两棵线段树之前需要判断一下是否是联通块
那么,树顶的编号究竟是要存在哪就有点争议了
笔者选择保存在并查集里作为公共祖先,这样的话 root[ i ] 就只保存每个结点最初那棵线段树的 rt 编号,而实时的树顶编号则是 find(root[i])
值得一提的是,我写的时候这里并查集居然出事了
原先写的并查集是 “pre[x]为 0 则为当前点,否则转至pre[x]寻找pre[pre[x]]” 然而在第7个点发生了RE 于是改成黄学长风格的并查集 初始化 pre[x] = x 才算解决了问题
此外就没有什么其他问题了
Code
1 #include<stdio.h> 2 #define maxn 1000000 3 using namespace std; 4 5 int TIM,chart[maxn],root[maxn],n,m,q,rank[maxn]; 6 7 struct node{ 8 int L,R,lc,rc,sum; 9 }T[maxn*5]; 10 11 int build(int L,int R,int pos){ 12 int rt = ++TIM; 13 T[rt].L = L, T[rt].R = R; 14 if(L == R){ T[rt].sum = 1; return rt; } 15 int mid = (L+R)>>1; 16 if(pos <= mid) T[rt].lc = build(L,mid,pos); 17 else T[rt].rc = build(mid+1,R,pos); 18 T[rt].sum = T[T[rt].lc].sum+T[T[rt].rc].sum; 19 return rt; 20 } 21 22 int query(int rt,int pos){ 23 if(!rt || pos > T[rt].sum) return -1; 24 if(T[rt].L == T[rt].R) return chart[T[rt].L]; 25 if(pos <= T[T[rt].lc].sum) return query(T[rt].lc,pos); 26 else return query(T[rt].rc,pos-T[T[rt].lc].sum); 27 } 28 29 int Merge(int rt_a,int rt_b){ 30 if(!rt_a||!rt_b) return rt_a^rt_b; 31 T[rt_a].lc = Merge(T[rt_a].lc,T[rt_b].lc); 32 T[rt_a].rc = Merge(T[rt_a].rc,T[rt_b].rc); 33 T[rt_a].sum += T[rt_b].sum; 34 return rt_a; 35 } 36 37 int pre[maxn*4]; int find(int x){ 38 if(pre[x] == x) return x; 39 else{ pre[x] = find(pre[x]); return pre[x]; } 40 }void unite(int u,int v){ 41 if(find(root[u]) == find(root[v]) && root[u] == root[v]) return; 42 Merge(find(root[u]),find(root[v])); 43 pre[find(root[v])] = find(root[u]); 44 } 45 46 void Q(){ 47 int x,k; scanf("%d%d",&x,&k); 48 int ans = query(find(root[x]),k); 49 printf("%d\n",ans); 50 } 51 52 void B(){ 53 int u,v; scanf("%d%d",&u,&v); 54 if(!u && !v) return; 55 unite(u,v); 56 } 57 58 int main(){ 59 // freopen("input7.in","r",stdin); 60 // freopen("1.out","w",stdout); 61 62 scanf("%d%d",&n,&m); 63 64 for(int i = 1;i <= n;i++){ scanf("%d",&rank[i]); chart[rank[i]] = i; } 65 66 for(int i = 1;i <= n;i++) root[i] = build(1,n,rank[i]),pre[root[i]] = root[i]; 67 68 for(int i = 1;i <= m;i++){ 69 int u,v; scanf("%d%d",&u,&v); 70 if(!u && !v) continue; 71 unite(u,v); 72 } 73 74 // printf("root[34534]: %d\n",root[34534]); 75 76 scanf("%d",&q); 77 78 for(int i = 1;i <= q;i++){ 79 char ctr[2]; scanf("%s",ctr); 80 if(ctr[0] == 'Q') Q(); 81 else B(); 82 }return 233; 83 84 return 0; 85 }