poj2985線段樹求第k大的數(並查集)

The k-th Largest Group
Time Limit: 2000MS   Memory Limit: 131072K
Total Submissions: 7255   Accepted: 2335

Description

Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to group some of the cats. To do that, he first offers a number to each of the cat (1, 2, 3, …,n). Then he occasionally combines the group cat i is in and the group cat j is in, thus creating a new group. On top of that, Newman wants to know the size of the k-th biggest group at any time. So, being a friend of Newman, can you help him?

Input

1st line: Two numbers N and M (1 ≤ NM ≤ 200,000), namely the number of cats and the number of operations.

2nd to (m + 1)-th line: In each line, there is number C specifying the kind of operation Newman wants to do. If C = 0, then there are two numbers i and j (1 ≤ ij ≤ n) following indicating Newman wants to combine the group containing the two cats (in case these two cats are in the same group, just do nothing); If C = 1, then there is only one number k (1 ≤ k ≤ the current number of groups) following indicating Newman wants to know the size of the k-th largest group.

Output

For every operation “1” in the input, output one number per line, specifying the size of the kth largest group.

Sample Input

10 10
0 1 2
1 4
0 3 4
1 2
0 5 6
1 1
0 7 8
1 1
0 9 10
1 1

Sample Output

1
2
2
2
2

Hint

When there are three numbers 2 and 2 and 1, the 2nd largest number is 2 and the 3rd largest number is 1.

Source

POJ Monthly--2006.08.27, zcgzcgzcg
//線段樹每個節點維護的是group大小在這個區間的個數
//每次更新時,包含a[x]和a[y] group大小的區間分別減1
//而包含a[x]+a[y]的區間加1,詢問的時候,先詢問右子樹
//如果右子樹的cnt>=k,則去右子樹繼續查詢,否則去左子樹
//並且k-=rson的cnt。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

#define maxn 200005
#define ls(o) o<<1
#define rs(o) (o<<1)|1
#define Mid(a,b) (a + b)>>1
#define Fill(s,e,xx) for(int i=s;i<e;i++)a[i]=xx;

int fa[maxn],Rank[maxn],n,m;

void init(int n)//一定要記得初始化
{
    for(int i=0;i<=n;i++)fa[i]=i,Rank[i]=0;
}

int find_fa(int x)//查詢是否已經在同一組了
{
    if(fa[x]!=x)fa[x]=find_fa(fa[x]);
    return fa[x];
}

void Union(int x,int y)//在這裏並不需要這麼合併,^-^,算是無用吧
{
    x = find_fa(x);
    y = find_fa(x);
    if(x==y)return;
    if(Rank[x]<Rank[y]){
        fa[x] = y;
    }
    else{
        fa[y] = x;
        if(Rank[x]==Rank[y])Rank[x]++;
    }
}

struct line_tree{
    int l,r;//存儲區間信息
    int cnt;//存儲在該區間內,有多少區間在該區間內的group
}tree[maxn<<2];

void build(int tl,int tr,int o)
{
    tree[o].l=tl,tree[o].r=tr;
    if(tl==1)tree[o].cnt = n;
    else tree[o].cnt = 0;
    if(tl == tr){
        return;
    }
    int mid = Mid(tl,tr);
    build(tl,mid,ls(o));
    build(mid+1,tr,rs(o));
}

void Insert(int x,int o,int c)
{
    tree[o].cnt+=c;
    if(tree[o].l == tree[o].r)return;
    if(x<=tree[ls(o)].r)Insert(x,ls(o),c);
    else Insert(x,rs(o),c);
}

int query(int k,int o=1)
{
    if(tree[o].l==tree[o].r){
        printf("%d\n",tree[o].l);
        return 0;
    }
    if(k<=tree[rs(o)].cnt)query(k,rs(o));//右子樹有大於k個數,第k個數肯定在右邊。。。。//求的是第k大數。。。。所以從右子樹查詢
    else query(k-tree[rs(o)].cnt,ls(o));
}
int a[maxn];//存儲group信息
int  main()
{
    while(scanf("%d%d",&n,&m)==2){
        Fill(0,n+1,1);
        build(1,n,1);init(n);
        for(int i=0;i<m;i++){
            int com;
            scanf("%d",&com);
            if(com==0){
                int x,y;
                scanf("%d%d",&x,&y);
                x = find_fa(x),y=find_fa(y);
                if(x==y)continue;
                Insert(a[x],1,-1);
                Insert(a[y],1,-1);
                Insert(a[x]+a[y],1,1);
                fa[y] = x;//兩個一定要一致。。。。。。。。。。
                a[x]+=a[y];
            }
            else{
                int k;
                scanf("%d",&k);
                query(k);
            }
        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章