0
点赞
收藏
分享

微信扫一扫

POJ 2985 The k-th LargestGroup(Treap树)


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.

题意:

        有N只猫,开始每只猫都是一个小组,下面要执行M个操作,操作0 i j 是把i猫和j猫所属的小组合并,操作1 k 是问你当前第k大的小组大小是多少. 且k<=当前的最大组数.

分析:

        用并查集维护每只猫所属的集合,且维护集合中的节点数.

        然后假设集合S1和集合S2合并,就在Treap中删除V=|S1|的节点并删除V=|S2|的节点.并插入V=|S1|+|S2|的节点.(注意如果|S1|==1,不用删除,因为Treap只保存了合并之后的组大小)

        然后查询的时候就查询Treap中第K大的节点v值即可.这里要注意,如果K>Treap中的节点总数,就默认输出1.(因为我们Treap只保存了合并之后的组大小)

AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<queue>
#include<set>
#include<map>
#include<iomanip>
#include<math.h>
using namespace std;
typedef long long ll;
typedef double ld;
const int INF=0x3f3f3f3f;
using namespace std;
const int maxn = 2e5+999;
int n,m,cnt;
int pa[maxn];

struct node
{
node* ch[2];
int r,v,s;
node(int v):v(v)
{
r = rand();
s = 1;
ch[0] = ch[1] = NULL;
}

void maintain()
{
s = 1;
if(ch[0])s+=ch[0]->s;
if(ch[1])s+=ch[1]->s;
}

int cmp(int x)
{
if(x==v)return -1;
return x<v?0:1;
}
}*root;


void Rotate(node* &o,int d)
{
node *k = o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d] = o;
o->maintain();
k->maintain();
o=k;
}

void Insert(node* &o,int v)
{
if(o==NULL)o = new node(v);
else
{
int d = v<o->v? 0:1;
Insert(o->ch[d],v);
if(o->ch[d]->r > o->r)
Rotate(o,d^1);
}
o->maintain();
}

void Remove(node* &o,int v)
{
int d = o->cmp(v);
if(d == -1)
{
if(o->ch[0] && o->ch[1])
{
int d2 = o->ch[0]->r < o->ch[1]->r? 0 : 1;
Rotate(o,d2);
Remove(o->ch[d2],v);
}
else
{
node* u = o;
if(!o->ch[0])o=o->ch[1];
else o = o->ch[0];
delete u;
}
}
else Remove(o->ch[d],v);
if(o) o->maintain();
}

int kth(node *o,int k)
{

int s = (o->ch[1]==NULL)?0:o->ch[1]->s;
if(k==s+1)return o->v;
else if(k<=s)return kth(o->ch[1],k);
else return kth(o->ch[0],k-s-1);
}

int findset(int x)
{
return pa[x]<0? x : pa[x] = findset(pa[x]);
}
void unionset(int x,int y)
{
int rx = findset(x);
int ry = findset(y);
if(rx != ry)
{
if(pa[rx]!=-1)
{
Remove(root,-pa[rx]); /**根节点的pa放集合内元素数量的相反数*/
cnt--;
}
if(pa[ry]!=-1)Remove(root,-pa[ry]),cnt--;

Insert(root,-pa[rx]-pa[ry]);
cnt++;
pa[ry] += pa[rx];
pa[rx] = ry;
}
}

int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
root=NULL;
cnt=0;
memset(pa,-1,sizeof pa);
while(m--)
{
int op,i,j,k;
scanf("%d",&op);
if(op==0)
{
scanf("%d%d",&i,&j);
unionset(i,j);
}
else
{
scanf("%d",&k);
if(k>cnt)printf("1\n");
else
printf("%d\n",kth(root,k));
}
}

}
return 0;
}

 

举报

相关推荐

0 条评论