题意:
有N只猫,开始每只猫都是一个小组,下面要执行M个操作。
操作0 i j 是把i猫和j猫所属的小组合并。
操作1 k 是问你当前第k大的小组大小是多少. 且k<=当前的最大组数.
题解:
Treap实现名词树+并查集,并查集根节点root:pa[root] = -规模。
/**
treap求第k大
*/
#include<cstdlib>
#include<cstring>
#include<cstdio>
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)///第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]);cnt--;}/**根节点的pa放集合内元素数量的相反数*/
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;
}