[洛谷P3377]【模板】左偏树(可并堆)

题目大意:
给你n个小根堆,每个堆初始有一个元素。有两个操作:
1. 把第x个数和第y个数所在的堆合并(如果第x个数或第y个数已经被删除or它们已经在同一个堆内则忽略)
2. 输出第x个数所在堆的堆顶元素,并将这个堆顶元素弹出堆(如果第x个数不存在则输出-1)
如果第$a_i$个数与第$a_j$个数相同,则比较i与j的大小。
解题思路:
可并堆嘛。话说随机化期望值还是蛮高的,于是随机堆+并查集水过。
嗯……平板电视大法也不错呀,不过空间占用貌似有点多啊(还好还好没超)。

随机堆:

C++ Code:

#include<bits/stdc++.h>
int n,m,fa[100005],vis[100005];
inline int readint(){
	int c=getchar(),d=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return d;
}
struct Heap{
	Heap *ls,*rs;
	int s,id;
	Heap(int t=0,int num=0){
		ls=rs=NULL;
		s=t;
		id=num;
	}
	inline bool operator<(const Heap& rhs)const{
		if(s!=rhs.s)return s<rhs.s;
		return id<rhs.id;
	}
}*a[100005];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
Heap* merge(Heap*& a,Heap*& b){
	if(a==NULL)return b;
	if(b==NULL)return a;
	if((*b)<(*a))std::swap(a,b);
	if(rand()&1)a->rs=merge(a->rs,b);else
	a->ls=merge(a->ls,b);
	return a;
}
int Delete(Heap*& p){
	int id=p->id;
	p=merge(p->ls,p->rs);
	return id;
}
int main(){
	#ifdef LOCALJUDGE
	freopen("testdata.in","r",stdin);
	freopen("testdata.out","w",stdout);
	#endif
	srand(20170607);
	memset(vis,0,sizeof vis);
	n=readint(),m=readint();
	for(int i=1;i<=n;++i)fa[i]=i,a[i]=new Heap(readint(),i);
	while(m--){
		int opt=readint();
		if(opt==1){
			int x=readint(),y=readint();
			int a=find(x),b=find(y);
			if(vis[y]||vis[x]||!a||!b)continue;
			if(a!=b){
				fa[b]=a;
				::a[a]=merge(::a[a],::a[b]);
			}
		}else{
			int x=readint();
			int p=find(x);
			if(!p||vis[x]||a[p]==NULL){
				puts("-1");
				continue;
			}
			printf("%d\n",a[p]->s);
			int id=Delete(a[p]);
			vis[id]=1;
		}
	}
	return 0;
}

pbds:

C++ Code:

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
__gnu_pbds::priority_queue<pair<int,int>,greater<pair<int,int> >,__gnu_pbds::thin_heap_tag>a[100005];
inline int readint(){
	int c=getchar(),d=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())d=(d<<3)+(d<<1)+(c^'0');
	return d;
}
bool vis[100005];
int n=readint(),m=readint(),fa[100005];
inline int dad(int x){return x==fa[x]?x:fa[x]=dad(fa[x]);}
int main(){
	memset(vis,0,sizeof vis);
	for(int i=1;i<=n;++i)a[i].push(make_pair(readint(),i)),fa[i]=i;
	while(m--){
		int opt=readint();
		if(opt==1){
			int x=readint(),y=readint();
			if(vis[x]||vis[y])continue;
			x=dad(x),y=dad(y);
			if(x!=y){
				fa[y]=x;
				a[x].join(a[y]);
			}
		}else{
			int x=readint();
			int f=dad(x);
			if(vis[x]){
				puts("-1");
				continue;
			}
			pair<int,int>p=a[f].top();
			a[f].pop();
			printf("%d\n",p.first);
			vis[p.second]=1;
		}
	}
	return 0;
}

 

转载于:https://www.cnblogs.com/Mrsrz/p/8885249.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值