【BZOJ1455】罗马游戏(简单重拾左偏树)

点此看题面

大致题意: 左偏树板子。给定\(n\)个数,两种操作:合并两堆数;询问并删除某堆数中的最小值。

前言

暂时脱离网络流的苦海,写道水题轻松一下。。。

为什么会来做这道题?因为提交完另一道题后正好机房里有人在写此题,主要是看到了罗马,想着好久没写过左偏树了,就来写发板子。

大致想法

板子题还能有啥想法。。。。。。

只是用并查集维护连通性的时候,突发奇想用并查集的老祖宗来记录每个点所在左偏树的根,然后发现这样只要查询祖先就能求出堆中的最小值。于是最后就变成了用左偏树来维护并查集???(怎么感觉哪里不太对劲,可仔细一想又没啥问题emmm)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,a[N+5],p[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		I void readc(char& x) {W(isspace(x=tc()));}
		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
		#undef D
}F;
class UnionFindSet//神奇的并查集
{
	private:
		int f[N+5];
	public:
		I void Init() {for(RI i=1;i<=n;++i) f[i]=i;}//初始化
		I int fa(CI x) {return f[x]^x?f[x]=fa(f[x]):x;}//找老祖宗
		I void Union(CI x,CI y) {f[y]=x;}//合并
		I void Upt(CI x,CI y) {f[x]=f[y]=y;}//修改老祖宗(左偏树弹去堆顶后要修改根)
}U;
class LeftistTree//左偏树(用于维护并查集?)
{
	private:
		struct node {int F,D,S[2];I node() {F=D=S[0]=S[1]=0;}}O[N+5];
		I int Merge(RI x,RI y)//合并
		{
			if(!x||!y) return x|y;a[x]>a[y]&&swap(x,y),O[x].S[1]=Merge(O[x].S[1],y),
			O[O[x].S[1]].F=x,O[O[x].S[1]].D>O[O[x].S[0]].D&&swap(O[x].S[0],O[x].S[1]);
			return O[x].D=O[O[x].S[1]].D+1,x;
		}
	public:
		I LeftistTree() {O[0].D=-1;}
		I void Union(CI x,CI y) {a[x]<a[y]?U.Union(x,y):U.Union(y,x),Merge(x,y);}//合并,使并查集的老祖宗为权值较小的点(即堆顶)
		I void Pop(CI x)//弹出堆顶
		{
			p[x]=1,O[x].D=-1,O[O[x].S[0]].F=O[O[x].S[1]].F=0;//消除在左偏树上留下的痕迹
			RI y=Merge(O[x].S[0],O[x].S[1]);y&&(U.Upt(x,y),0);//把并查集中x所在连通块的老祖宗修改为当前根
		}
}T;
int main()
{
	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);U.Init();
	RI Qt,x,y,fx,fy;char op;F.read(Qt);W(Qt--) switch(F.readc(op),F.read(x),op)
	{
		case 'M':F.read(y),!p[x]&&!p[y]&&//如果都没被删掉
			(fx=U.fa(x))^(fy=U.fa(y))&&(T.Union(fx,fy),0);break;//不在同一堆中便合并
		case 'K':!p[x]?(F.writeln(a[y=U.fa(x)]),T.Pop(y)):F.writeln(0);break;//输出老祖宗的权值,然后弹出堆顶
	}return F.clear(),0;
}

猜你喜欢

转载自www.cnblogs.com/chenxiaoran666/p/BZOJ1455.html