题目链接:点击打开链接
题意:有两种操作,合并集合,查询第K大集合的元素个数。(总操作次数为2*10^5)
解法:
1、Treap
2、树状数组
|-二分找第K大数
|-二进制思想,逼近第K大数
3、线段树
4、。。。
Treap模板(静态数组)
#include <math.h> #include <time.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> const int maxNode = 500000 + 100; const int inf = 0x3f3f3f3f; struct Treap{ int root, treapCnt, key[maxNode], priority[maxNode], childs[maxNode][2], cnt[maxNode], size[maxNode]; Treap(){ root = 0; //编号从1开始,0表示为空结点 treapCnt = 1; priority[0] = INT_MAX; size[0] = 0; //srand(time(0)); //可以不用 } void update(int x){ size[x] = size[childs[x][0]] + cnt[x] + size[childs[x][1]]; } void rotate(int &x, int t){ int y = childs[x][t]; childs[x][t] = childs[y][1-t]; childs[y][1-t] = x; update(x); update(y); x = y; } void insert(int &x, int k){ if(x){ if(key[x] == k){ cnt[x]++; //允许有相同结点 }else{ int t = key[x] < k; insert(childs[x][t], k); if(priority[childs[x][t]] < priority[x]){ rotate(x, t); } } }else{ x = treapCnt++; key[x] = k; cnt[x] = 1; priority[x] = rand(); childs[x][0] = childs[x][1] = 0; } update(x); } void erase(int &x, int k){ if(key[x] == k){ if(cnt[x] > 1){ cnt[x]--; }else{ if(childs[x][0]==0 && childs[x][1]==0){ x = 0; return ; } int t = priority[childs[x][0]] > priority[childs[x][1]]; rotate(x, t); erase(x, k); } }else{ erase(childs[x][key[x]<k], k); } update(x); } int find(int &x, int k){ if(x){ if(key[x] == k ) return 1; find(childs[k < key[x]], k); } return 0; } int kth(int &x, int k){ if(k <= size[childs[x][0]]){ return kth(childs[x][0], k); } k -= size[childs[x][0]] + cnt[x]; if(k <= 0){ return key[x]; } return kth(childs[x][1], k); } int rank(int &x, int k) { int ret = 0; if(key[x] > k) ret = rank(childs[x][0], k); else if(key[x] == k) return size[childs[x][0]] + 1; else ret = size[childs[x][0]] + 1 +rank(childs[x][1], k) ; return ret; } }tp; int f[maxNode], tot[maxNode]; int findset(int x) { return f[x]==x? x : f[x] = findset(f[x]); } int main() { int i, k, x, y, n, m; scanf("%d%d",&n,&m); for(i=1; i<=n; ++i) { f[i] = i; tot[i] = 1; tp.insert(tp.root, 1); /*以每一只猫为单位建树*/ } for(i=1; i<=m; ++i) { scanf("%d",&k); if(!k){ scanf("%d%d",&x, &y); x = findset(x); y = findset(y); if(x == y) continue; f[y] = x; tp.erase(tp.root, tot[x]); tp.erase(tp.root, tot[y]); tot[x] += tot[y]; tot[y] = 0; tp.insert(tp.root, tot[x]); n--; //合并两组后,组数减一 }else{ scanf("%d", &k); printf("%d\n", tp.kth(tp.root, n-k+1)); } } return 0; }
树状数组(二分)
#include <cstdio> const int maxn = 200000; int c[maxn + 100],a[maxn + 100], f[maxn + 100];//a[i]表示值为i的数的个数 int lowbit(int x) {return x&-x;} void update(int i, int val){ for(; i<=maxn; i += lowbit(i)) c[i] += val;} int getsum(int i){int ret = 0; for(i; i>0; i -= lowbit(i)) ret += c[i]; return ret;} int findset(int x) {return f[x]==x?f[x]:f[x] = findset(f[x]); } int main() { int i,n,m,q,x,y,k,l,r; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) f[i]=i; for(i=1;i<=n;i++) a[i]=1; update(1,n);//初始状态值为1的数有n个 int num=n; for(i=1;i<=m;i++){ scanf("%d",&q); if(q==0){ scanf("%d%d",&x,&y); x=findset(x); y=findset(y); if(x==y) continue; update(a[x],-1); update(a[y],-1); update(a[x]+a[y],1); f[y]=x; a[x]+=a[y]; num--;//合并集合 }else{ //二分找第k小的数 scanf("%d",&k); k=num-k+1;//转换为找第k小的数 int l = 1, r = n; while(l <= r) { int mid = (l + r)>>1; if(getsum(mid) >= k) r = mid - 1; else l = mid + 1; } printf("%d\n", l); } } return 0; }
树状数组(二进制,逼近)
#include <cstdio> const int maxn = 200000; int c[maxn + 100],a[maxn + 100], f[maxn + 100];//a[i]表示值为i的数的个数 int lowbit(int x) {return x&-x;} void update(int i, int val){ for(; i<=maxn; i += lowbit(i)) c[i] += val;} int findset(int x) {return f[x]==x?f[x]:f[x] = findset(f[x]); } int find_kth(int k) { int ans= 0, cnt = 0, i; for(i=20; i >= 0; --i)///利用二进制的思想,把答案用一个二进制数来表示 { ans += (1<<i); if(ans > maxn || cnt + c[ans] >= k) ///这里大于等于k的原因是可能会有很多个数都满足cnt + c[ans] >= k,所以找的是最大的满足cnt+c[ans]<k的ans ans -= (1<<i); else cnt += c[ans];///cnt用来累加比当前ans小的总组数 }///求出的ans是累加和(即小于等于ans的数的个数)小于k的情况下ans的最大值,所以ans+1就是第k大的数 return ans + 1; } int main() { int i,n,m,q,x,y,k,l,r; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) f[i]=i; for(i=1;i<=n;i++) a[i]=1; update(1,n);//初始状态值为1的数有n个 int num=n; for(i=1;i<=m;i++){ scanf("%d",&q); if(q==0){ scanf("%d%d",&x,&y); x=findset(x); y=findset(y); if(x==y) continue; update(a[x],-1); update(a[y],-1); update(a[x]+a[y],1); f[y]=x; a[x]+=a[y]; num--;//合并集合 }else{ scanf("%d",&k); k=num-k+1;//转换为找第k小的数 printf("%d\n",find_kth(k)); } } return 0; }
线段树
#include <cstdio> #define LL(x) (x<<1) #define RR(x) (x<<1|1) const int maxn = 200005; int f[maxn], num[maxn]; ///num[i]表示值为i的数的个数 int findset(int x) {return f[x]==x?f[x]:f[x] = findset(f[x]); } int n, Q; struct node { int l, r; int sum; ///记录:元素个数为i[l<=i<=r]的Group的总个数 }tree[maxn*4]; void build(int rt, int l, int r){ tree[rt].l = l; tree[rt].r = r; if(l == 1) tree[rt].sum = n; else tree[rt].sum = 0; if(l == r) return ; int mid = (l + r)>>1; build(LL(rt), l, mid); build(RR(rt), mid+1, r); } void update(int rt, int pos, int val){ tree[rt].sum += val; if(tree[rt].l == tree[rt].r) return; int mid = (tree[rt].l + tree[rt].r)>>1; if(pos <= mid) update(LL(rt), pos, val); else update(RR(rt), pos, val); } int query(int rt, int k){ //寻找第k大的数 if(tree[rt].l == tree[rt].r) return tree[rt].l; if(k<=tree[RR(rt)].sum) return query(RR(rt), k); else return query(LL(rt), k - tree[RR(rt)].sum); } int main(){ scanf("%d%d",&n,&Q); for(int i = 1; i <= n; i ++){ num[i] = 1; f[i] = i; } int op,a,b; build(1,1,n); for(int i = 1;i <= Q; i ++){ scanf("%d",&op); if(op == 0){ scanf("%d%d",&a,&b); int x = findset(a); int y = findset(b); if(x == y) continue ; update(1, num[x], -1); update(1, num[y], -1); update(1, num[x] + num[y], 1); num[x] += num[y]; f[y] = x; }else{ scanf("%d",&a); printf("%d\n",query(1, a)); } } return 0; }
poj 2985 The k-th Largest Group 求第K大数 Treap, Binary Index Tree, Segment Tree,布布扣,bubuko.com
poj 2985 The k-th Largest Group 求第K大数 Treap, Binary Index Tree, Segment Tree
原文地址:http://blog.csdn.net/yew1eb/article/details/26667993