【BZOJ2733】永无乡(平衡树)

题目大意

n个点,每个点有个重要度排名,初始有一些点是连着的,Q个操作,Q询问a所在连通块重要度排名为b的点编号,B连接ab。

题解

每个连通块为一个平衡树(可查询第k大),每次连接用暴力合并启发式合并来合并两颗平衡树,用并查集判断点属于哪个连通块。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAXN 100005
struct Node
{
	int val,pri,sz;
	Node *son[2];
	Node(){}
	Node(int v){val=v;pri=rand();sz=1;son[0]=son[1]=NULL;}
	void Update()
	{
		sz=1;
		if(son[0])sz+=son[0]->sz;
		if(son[1])sz+=son[1]->sz;
	}
}nodes[MAXN],*cur=nodes;
struct Treap
{
	Node *root;
	Treap(){root=NULL;}
	int cmp(int x,int y){return x==y?-1:x>y;}
	int size(){return root?root->sz:0;}
	Node *newNode(int val)
	{
		*cur=Node(val);
		return cur++;
	}
	void Rotate(Node *&x,int d)
	{
		Node *y=x->son[!d];
		x->son[!d]=y->son[d];
		y->son[d]=x;
		x->Update();
		y->Update();
		x=y;
	}
	void Insert(Node *&now,Node *x)
	{
		if(now==NULL)
			now=x;
		else
		{
			int d=cmp(x->val,now->val);
			Insert(now->son[d],x);
			if(now->son[d]->pri<now->pri)
				Rotate(now,!d);
			now->Update();
		}
	}
	void Insert(Node *x)
	{x->son[0]=x->son[1]=NULL;x->Update();Insert(root,x);}
	Node *FindKth(int k)
	{
		Node *now=root;
		if(!root)return NULL;
		int rank=(now->son[0]?now->son[0]->sz:0)+1;
		while(now)
		{
			if(rank==k)
				return now;
			else if(rank>k)
			{
				now=now->son[0];
				if(!now)break;
				rank-=(now->son[1]?now->son[1]->sz:0)+1;
			}
			else
			{
				now=now->son[1];
				if(!now)break;
				rank+=(now->son[0]?now->son[0]->sz:0)+1;
			}
		}
		return NULL;
	}
	void Merge(Treap &out,Node *now)
	{
		if(now==NULL)return;
		Merge(out,now->son[0]);
		Merge(out,now->son[1]);
		out.Insert(now);
	}
	void Merge(Treap &out)
	{Merge(out,root);root=NULL;}
}T[MAXN];
int S[MAXN];
int Root(int x){return S[x]==x?x:S[x]=Root(S[x]);}
void Merge(int a,int b)
{
	int x=Root(a),y=Root(b);
	if(a==b)return;
	if(T[x].size()<T[y].size())swap(x,y);
	S[y]=x;
	T[y].Merge(T[x]);
}
int main()
{
	int a,b,n,m,Q;
	char op[3];
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		nodes[i]=Node(x);
		T[i].Insert(nodes+i);
	}
	for(int i=1;i<=n;i++)S[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		Merge(a,b);
	}
	scanf("%d",&Q);
	for(int q=1;q<=Q;q++)
	{
		scanf("%s%d%d",op,&a,&b);
		if(op[0]=='Q')
		{
			Node *ans=T[Root(a)].FindKth(b);
			if(!ans)printf("-1\n");
			else printf("%d\n",ans-nodes);
		}
		else
			Merge(a,b);
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值