POJ2985 The k-th Largest Group(Splay)

有n组猫,m个操作。初始的时候每组猫有一只,有两种操作,可以把两组猫合并为一组,或者查询猫数第k多的组。

对于合并猫我们开一个并查集,检查那些已经在一个集合中的猫。首先在splay树中插入每组猫的数,当合并的时候,就从树中删去那两组猫,在插入它们的和。为了方便删除,在初始化的时候要插入0和n+1两个节点。

#include<cstdio>
#include<cctype>
#define MAXN 200010
void GET(int &t)
{
	char c;
	t = 0;
	do{c = getchar();}while(!isdigit(c));
	while(isdigit(c)){t = t*10+c-'0'; c = getchar();}
}
int fa[MAXN],sz[MAXN],cnt[MAXN],tot,root,ch[MAXN][2],v[MAXN],n,m,op,sat[MAXN],amount[MAXN];
int find(int x)
{
	if(sat[x] == x) return sat[x];
	else return sat[x] = find(sat[x]);
}
void update(int x)
{
	sz[x] = sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void rotate(int x)
{
	int y = fa[x],z = fa[y],f = (ch[y][1]==x);
	ch[y][f] = ch[x][!f];
	if(ch[y][f]) fa[ch[y][f]] = y;
	ch[x][!f] = y,fa[y] = x;
	fa[x] = z;
	if(z) ch[z][ch[z][1]==y] = x;
	update(y);
}
void splay(int x,int goal)
{
	for(int y; (y=fa[x])!=goal; rotate(x))
	{
		int z = fa[y];
		if(z != goal)
		{
			if((ch[z][0]==y)==(ch[y][0]==x)) rotate(y);
			else rotate(x);
		}
	}
	if(goal == 0) root = x;
	update(x);
}
void insert(int val)
{
	int x = root,y = 0,f = 0;
	while(x != 0)
	{
		if(v[x] == val) {++cnt[x]; break;}
		f = v[x]<val;
		y = x;
		x = ch[x][f];
	}
	if(x == 0)
	{
		x = ++tot;
		v[x] = val;
		sz[x] = cnt[x] = 1;
		fa[x] = y;
		if(y) ch[y][f] = x;
	}
	splay(x,0);
}
int nxt(int val,bool flag)
{
	int x = root,y = 0;
	while(x != 0)
	{
		y = x;
		if(v[x] == val) break;
		x = ch[x][v[x]<val];
	}
	if((v[y]>val&&flag==1)||(v[y]<val&&flag==0)) return y;
	splay(y,0);
	int tmp = ch[y][flag];
	while(ch[tmp][!flag])
		tmp = ch[tmp][!flag];
	return tmp;
}
void del(int val)
{
	int x = nxt(val,0),y = nxt(val,1);
	splay(x,0);
	splay(y,x);
	int z = ch[y][0];
	if(z)
	{
		if(cnt[z] > 1)
		{
			cnt[z]--;
			splay(z,0);
		}
		else 
		{
			ch[y][0] = 0;
			cnt[z] = 0;
			splay(y,0);
		}
	}
}
int kth(int k)
{
	int y = root,x;
	if(k > sz[root]) return 0;
	while(1)
	{
		x = ch[y][1];
		if(sz[x]+cnt[y] < k)
		{
			k -= sz[x]+cnt[y];
			y = ch[y][0];
		}
		else if(sz[x]>=k) y = x;
		else return v[y];
	}
}
int main()
{
	GET(n);
	GET(m);
	insert(0);
	insert(n+1);
	for(int i = 1; i <= n; i++) sat[i] = i,insert(1),amount[i] = 1;
	int a,b,c;
	for(int i = 1; i <= m; i++)
	{
		GET(op);
		if(op)
		{
			GET(a);
			printf("%d\n",kth(a+1));
		}
		else
		{
			GET(a),GET(b);
			a = find(a);
			b = find(b);
			if(a == b) continue;
			del(amount[a]);
			del(amount[b]);
			c = amount[a]+amount[b];
			sat[b] = a;
			amount[a] = c;
			amount[b] = c;
			insert(c);
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值