poj 2985 The k-th Largest Group 求第K大数 Treap, Binary Index Tree, Segment Tree

来源:互联网 发布:发票验旧需要什么数据 编辑:程序博客网 时间:2024/06/03 19:55

题目链接:点击打开链接

题意:有两种操作,合并集合,查询第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;}


0 0