POJ--2985(树状数组 / Treap +并查集)
2015-03-16 19:52:57
思路:一道比较裸的插入不断插入数值、删除数值 + 查询第k大的题目。
(1)对于合并操作,可以采用并查集来维护,并维护一个num值来表示group总数。
(2)对于查询第k大的操作
(i)可以用树状数组维护+二分查找来不断逼近第k大
(ii)可以利用树状数组本身c[]数组的特性来倍增逼近第k大
(iii)用treap来查询第k大
(iv)当然还可以用线段树.....
方法1:树状数组+二分
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const int MAXN = 300010; 27 28 int N,M,num; 29 int fa[MAXN],sz[MAXN]; 30 31 struct BIT{ 32 int c[MAXN]; 33 int Lowbit(int x){ 34 return x & (-x); 35 } 36 void clear(){ 37 MEM(c,0); 38 } 39 void Update(int x,int d){ 40 while(x <= N){ 41 c[x] += d; 42 x += Lowbit(x); 43 } 44 } 45 int Getsum(int x){ 46 int res = 0; 47 while(x){ 48 res += c[x]; 49 x -= Lowbit(x); 50 } 51 return res; 52 } 53 }bt; 54 55 int Find(int x){ 56 return fa[x] == x ? x : fa[x] = Find(fa[x]); 57 } 58 59 int main(){ 60 int a,b,c,k; 61 scanf("%d%d",&N,&M); 62 bt.Update(1,N); 63 REP(i,N) fa[i] = i,sz[i] = 1; 64 num = N; 65 REP(i,M){ 66 scanf("%d",&c); 67 if(c == 0){ 68 scanf("%d%d",&a,&b); 69 int x = Find(a); 70 int y = Find(b); 71 if(x == y) continue; 72 bt.Update(sz[x],-1); 73 bt.Update(sz[y],-1); 74 fa[x] = y; 75 sz[y] += sz[x]; 76 bt.Update(sz[y],1); 77 num--; 78 } 79 else{ 80 scanf("%d",&k); 81 k = num + 1 - k; 82 int l = 1,r = N; 83 while(l <= r){ 84 int mid = (l + r) / 2; //getmid(l,r); 85 if(bt.Getsum(mid) >= k) r = mid - 1; 86 else l = mid + 1; 87 } 88 printf("%d\n",l); 89 } 90 } 91 return 0; 92 }
方法2:树状数组
这个方法很奇妙,用到了c[]数组本身的倍增特性,哎,果然要善于发现数据结构的潜力才行~
首先,我们知道 c [a] 保存的是 arr[a-lowbit(a)+1] + arr[a-lowbit(a)+2] + .... + arr[a] 这个和。
假设最终第k大的group大小为ans,那么c[ans] >= k,将ans二进制拆分为2^p1 + 2^p2 + ... + 2^pm
那么,c[ans] = c[2^p1] + c[2^p2] + ... + c[2^pm],那么我们只要倒序枚举2的幂来找出p1~pm的值就行。
【 如:for(int i = MAX_LOG; i >= 0; --i) , 2的幂为(1 << i) 】
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const int MAXN = 200010; 27 const int MAX_LOG = log(MAXN) / log(2); 28 29 int N,M,num; 30 int fa[MAXN],sz[MAXN]; 31 32 struct BIT{ 33 int c[MAXN]; 34 void clear(){ MEM(c,0); } 35 int Lowbit(int x){ return x & (-x); } 36 void Update(int x,int d){ 37 while(x <= N){ 38 c[x] += d; 39 x += Lowbit(x); 40 } 41 } 42 int Getsum(int x){ 43 int res = 0; 44 while(x){ 45 res += c[x]; 46 x -= Lowbit(x); 47 } 48 return res; 49 } 50 }bt; 51 52 int Find(int x){ return fa[x] == x ? x : fa[x] = Find(fa[x]); } 53 void Union(int a,int b){ 54 int x = Find(a),y = Find(b); 55 if(x != y){ 56 bt.Update(sz[x],-1); 57 bt.Update(sz[y],-1); 58 fa[x] = y; 59 sz[y] += sz[x]; 60 bt.Update(sz[y],1); 61 num--; 62 } 63 } 64 65 int Find_kth(int k){ 66 int res = 0,cnt = 0; 67 for(int i = MAX_LOG; i >= 0; --i){ 68 res += (1 << i); 69 if(res > N || cnt + bt.c[res] >= k) res -= (1 << i); 70 else cnt += bt.c[res]; 71 } 72 return res + 1; 73 } 74 75 int main(){ 76 int a,b,c,k; 77 while(scanf("%d%d",&N,&M) != EOF){ 78 bt.clear(); 79 bt.Update(1,N); 80 num = N; 81 REP(i,N) fa[i] = i,sz[i] = 1; 82 while(M--){ 83 scanf("%d",&c); 84 if(c == 0){ 85 scanf("%d%d",&a,&b); 86 Union(a,b); 87 } 88 else{ 89 scanf("%d",&k); 90 k = num + 1 - k; 91 printf("%d\n",Find_kth(k)); 92 } 93 } 94 } 95 return 0; 96 }
方法3:Treap
这题是treap的启蒙题。
用treap来维护group大小的值,并且记录相同的值的个数,还要记录下以每个节点为根的子树的节点总数,然后就是在二叉搜索树上面找第k大了。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const int MAXN = 200010; 27 28 int N,M,num; 29 int fa[MAXN],sz[MAXN]; 30 31 struct Treap{ 32 int root,tcnt; 33 int key[MAXN],pro[MAXN],cnt[MAXN],sz[MAXN],son[MAXN][2]; 34 void clear(){ 35 root = 0; 36 tcnt = 0; 37 pro[0] = INF; 38 sz[0] = 0; 39 } 40 void Update(int x){ 41 sz[x] = cnt[x] + sz[son[x][0]] + sz[son[x][1]]; 42 } 43 void Rotate(int &x,int t){ 44 int y = son[x][t]; 45 son[x][t] = son[y][1 - t]; 46 son[y][1 - t] = x; 47 Update(x); 48 Update(y); 49 x = y; 50 } 51 void _Insert(int &x,int k){ 52 if(x){ 53 if(key[x] == k) ++cnt[x]; 54 else{ 55 int t = k > key[x]; 56 _Insert(son[x][t],k); 57 if(pro[son[x][t]] < pro[x]) Rotate(x,t); 58 } 59 } 60 else{ 61 x = ++tcnt; 62 key[x] = k; 63 cnt[x] = 1; 64 pro[x] = rand(); //随机化优先级 65 son[x][0] = son[x][1] = 0; 66 } 67 Update(x); 68 } 69 void _Erase(int &x,int k){ 70 if(key[x] == k){ 71 if(cnt[x] > 1) cnt[x]--; 72 else{ 73 if(son[x][0] == 0 && son[x][1] == 0){ 74 x = 0; 75 return; 76 } 77 int t = pro[son[x][0]] > pro[son[x][1]]; 78 //没有左儿子t = 1 , 没有右儿子t = 0 79 Rotate(x,t); 80 _Erase(x,k); 81 } 82 } 83 else _Erase(son[x][k > key[x]],k); 84 Update(x); 85 } 86 int _Get_kth(int &x,int k){ 87 if(k <= sz[son[x][0]]) 88 return _Get_kth(son[x][0],k); 89 k -= sz[son[x][0]] + cnt[x]; 90 if(k <= 0) return key[x]; 91 return _Get_kth(son[x][1],k); 92 } 93 void Insert(int k){ 94 _Insert(root,k); 95 } 96 void Erase(int k){ 97 _Erase(root,k); 98 } 99 int Get_kth(int k){ 100 return _Get_kth(root,k); 101 } 102 }tp; 103 104 int Find(int x){ 105 return fa[x] == x ? x : fa[x] = Find(fa[x]); 106 } 107 108 void Union(int a,int b){ 109 int x = Find(a),y = Find(b); 110 if(x != y){ 111 tp.Erase(sz[x]); 112 tp.Erase(sz[y]); 113 fa[x] = y; 114 sz[y] += sz[x]; 115 tp.Insert(sz[y]); 116 num--; 117 } 118 } 119 120 int main(){ 121 int a,b,c,k; 122 scanf("%d%d",&N,&M); 123 tp.clear(); 124 REP(i,N) fa[i] = i,sz[i] = 1,tp.Insert(1); 125 num = N; 126 while(M--){ 127 scanf("%d",&c); 128 if(c == 0){ 129 scanf("%d%d",&a,&b); 130 Union(a,b); 131 } 132 else{ 133 scanf("%d",&k); 134 k = num + 1 - k; 135 printf("%d\n",tp.Get_kth(k)); 136 } 137 } 138 return 0; 139 }