[PKU 2985] 平衡树&并查集
{
数据结构不常常写会手生的
于是就找了一个数据结构题写写
题目不错 是并查集和平衡树or线段树的结合
还有...poj更新域名了
我现在才知道...
最近光忙着写自己的程序了
好久没上了
}
原题 http://poj.org/problem?id=2985
题意
给定1-n范围内的n个数(n<=20w)
一开始每个数分属一个一元集
要求设计程序支持以下操作
将任意2个数及其所在集合合并
如果2个数已在同一个集合 则不操作
或是查询当前第k大的集合的大小
这是典型的数据结构题
要求支持很多操作 来完成几件事
当然这里只需要2个操作
查询集合 合并集合 和 查询第k大数(select)
前两个是并查集的操作
(并查集的相关操作 不再赘述 就引用一下别人的介绍吧 这位园友写得很好)
后面一个可以用线段树or平衡树解决
我曾经用线段树写过查询区间第k大(小)数的程序 具体参见这里
由于这里是查询整体 第k大数 我选择了比较好写的SBT来解决
(如果是区间问题 我可能写Splay 毕竟我还没用Splay解决过这个问题
碰到了再说吧 这题由于节点重复 Splay反而会比SBT快一点)
想了解更多关于SBT和Splay的内容 请移步这里
如果对并查集和平衡树都很了解的话
这个问题就不是很难了
我们用平衡树保存每个集合的轶 就是集合的大小
每次查询就是查询平衡树内第k大的元素
然后是集合操作 我们在并查集操作的基础上进行修改
由于加入了平衡树 对集合的修改必然伴随着对平衡树的修改
如果用并查集判断出2个数分属不同集合
就先在平衡树内删除2个集合的轶
平衡树内可能存在多个一样的轶
我们随便删掉一个即可 这并不影响我们以后的操作
然后合并2个集合 再在平衡树中插入新集合的轶
这样问题就解决了
给出我的代码
1 const max=500000;
2 maxn=200000;
3 var s,l,r,nr:array[0..max]of longint;
4 f,rk:array[1..maxn]of longint;
5 n,m,i,t,tt,x,y,z,fx,fy:longint;
6 function gf(x:longint):longint;
7 begin
8 if f[x]=x then exit(x);
9 f[x]:=gf(f[x]); exit(f[x]);
10 end;
11 procedure zig(var x:longint);
12 var y:longint;
13 begin
14 y:=l[x]; l[x]:=r[y]; r[y]:=x;
15 s[y]:=s[x]; s[x]:=s[l[x]]+s[r[x]]+1;
16 x:=y;
17 end;
18 procedure zag(var x:longint);
19 var y:longint;
20 begin
21 y:=r[x]; r[x]:=l[y]; l[y]:=x;
22 s[y]:=s[x]; s[x]:=s[l[x]]+s[r[x]]+1;
23 x:=y;
24 end;
25 procedure maintain(var x:longint; flag:boolean);
26 begin
27 if flag
28 then begin
29 if s[l[l[x]]]>s[r[x]] then zig(x)
30 else if s[r[l[x]]]>s[r[x]]
31 then begin zag(l[x]); zig(x); end
32 else exit;
33 end
34 else begin
35 if s[r[r[x]]]>s[l[x]] then zag(x)
36 else if s[l[r[x]]]>s[l[x]]
37 then begin zig(r[x]); zag(x); end
38 else exit;
39 end;
40 maintain(l[x],true); maintain(r[x],true);
41 maintain(x,true); maintain(x,false);
42 end;
43 procedure insert(var x:longint; v:longint);
44 begin
45 if x=0
46 then begin
47 inc(tt); x:=tt;
48 l[x]:=0; r[x]:=0; s[x]:=1;
49 nr[x]:=v;
50 end
51 else begin
52 inc(s[x]);
53 if v>=nr[x]
54 then insert(l[x],v)
55 else insert(r[x],v);
56 maintain(x,v>=nr[x]);
57 end;
58 end;
59 function delete(var x:longint; v:longint):longint;
60 begin
61 dec(s[x]);
62 if (nr[x]=v)or(v>nr[x])and(l[x]=0)or(v<nr[x])and(r[x]=0)
63 then begin
64 delete:=nr[x];
65 if (l[x]=0)or(r[x]=0)
66 then x:=l[x]+r[x]
67 else nr[x]:=delete(r[x],nr[x]+1);
68 end
69 else if v>nr[x]
70 then delete:=delete(l[x],v)
71 else delete:=delete(r[x],v);
72 end;
73 function select(x,k:longint):longint;
74 begin
75 if s[l[x]]+1=k then select:=nr[x]
76 else if k<=s[l[x]] then select:=select(l[x],k)
77 else select:=select(r[x],k-s[l[x]]-1);
78 end;
79 procedure out(X:longint);
80 begin
81 if x=0 then exit;
82 out(l[x]);
83 write(nr[x],' ');
84 out(r[x]);
85 end;
86 begin
87 assign(input,'cat.in'); reset(input);
88 assign(output,'cat.out'); rewrite(output);
89 readln(n,m);
90 t:=0; tt:=0; s[0]:=0;
91 for i:=1 to n do
92 begin
93 f[i]:=i; rk[i]:=1;
94 insert(t,1);
95 end;
96 for i:=1 to m do
97 begin
98 read(z);
99 if z=1
100 then begin
101 readln(x);
102 //out(t); writeln;
103 writeln(select(t,x));
104 end
105 else begin
106 readln(x,y);
107 fx:=gf(x); fy:=gf(y);
108 if fx=fy then continue;
109 //out(t); writeln;
110 delete(t,rk[fx]); delete(t,rk[fy]);
111 insert(t,rk[fx]+rk[fy]);
112 if rk[fx]<rk[fy]
113 then begin f[fx]:=fy; rk[fy]:=rk[fx]+rk[fy]; end
114 else begin f[fy]:=fx; rk[fx]:=rk[fx]+rk[fy]; end;
115 end;
116 end;
117 close(input); close(output);
118 end.
119
最后提一句
平衡树的调试 输出整个序列最有用!
Bob Han 原创 转载请注明出处
posted on 2010-10-01 17:57 Master_Chivu 阅读(1987) 评论(4) 编辑 收藏 举报