P3377 【模板】左偏樹/可並堆

liuboom發表於2024-12-06

P3377 【模板】左偏樹/可並堆

【模板】左偏樹/可並堆

題目描述

如題,一開始有 \(n\) 個小根堆,每個堆包含且僅包含一個數。接下來需要支援兩種操作:

  1. 1 x y:將第 \(x\) 個數和第 \(y\) 個數所在的小根堆合併(若第 \(x\) 或第 \(y\) 個數已經被刪除或第 \(x\) 和第 \(y\) 個數在同一個堆內,則無視此操作)。

  2. 2 x:輸出第 \(x\) 個數所在的堆最小數,並將這個最小數刪除(若有多個最小數,優先刪除先輸入的;若第 \(x\) 個數已經被刪除,則輸出 \(-1\) 並無視刪除操作)。

輸入格式

第一行包含兩個正整數 \(n, m\),分別表示一開始小根堆的個數和接下來操作的個數。

第二行包含 \(n\) 個正整數,其中第 \(i\) 個正整數表示第 \(i\) 個小根堆初始時包含且僅包含的數。

接下來 \(m\) 行每行 \(2\) 個或 \(3\) 個正整數,表示一條操作,格式如下:

操作 \(1\)1 x y

操作 \(2\)2 x

輸出格式

輸出包含若干行整數,分別依次對應每一個操作 \(2\) 所得的結果。

提示

【資料規模】

對於 \(30\%\) 的資料:\(n\le 10\)\(m\le 10\)
對於 \(70\%\) 的資料:\(n\le 10^3\)\(m\le 10^3\)
對於 \(100\%\) 的資料:\(n\le 10^5\)\(m\le 10^5\),初始時小根堆中的所有數都在 int 範圍內。

定義:

外節點: 對於節點x,若ls[x]=0 or rs[x]=0 則稱x為外節點

距離: 對於一個節點x,dis[x]的定義是:
x子樹中距x最近的外節點的距離

----------------------------------------------------

左偏樹的基本性質:

1.左偏樹具有堆的性質:
如本題中,要求維護一個小根堆:
則有:\(\forall\)x ,v[x]<=v[ls],v[x]<=v[rs].

2.左偏樹具有左偏性質: 不然為什麼叫左偏樹
\(\forall\)x dis[ls[x]]>=dis[rs[x]].

基本結論:

1.dis[x]=dis[rs[x]]+1 (顯然)

2.距離為𝑛的左偏樹至少2^(n+1)-1有個結點。此時該左偏樹的形態是一棵滿二叉樹。

3.有n個的結點的左偏樹的根節點的距離是log(n)的

合併:

merge(x,y) 為合併操作,其返回值為合併x,y後的根節點

注意:如果x,y有一個為空,則直接返回另一個

1,令v[x]<v[y],若不滿足,則交換,此時,新的根節點為x(小根堆中)

2.將y與x的右兒子合併,合併後的返回值(根)成為x的右兒子

3.重複上述操作

對於節點x,求其所在的堆:

顯然,並查集維護即可

對於本題特別要注意的是,如果查詢時這個點被刪除了,則輸出-1

所以我們開一個陣列del[x],記錄x是否被刪除

然後這題就做完了

Code

#include<bits/stdc++.h>
const int N=1e5+5;
using namespace std;
int n,m;
struct Tree{
	int id,v;
	bool operator<(const Tree &tt)const {
		return v==tt.v ? id<tt.id :v<tt.v; 
	}
	bool operator>(const Tree &tt)const {
		return v==tt.v ? id>tt.id :v>tt.v; 
	}
	Tree(int id_=0,int v_=0)
	{
		id=id_,v=v_;
	}
}t[N<<1];
int ls[N],rs[N],rt[N],dis[N];
bool del[N];
int merge(int x,int y)
{
	if(!x)return y;
	if(!y)return x;
	if(t[y]<t[x])swap(x,y);
	rs[x]=merge(rs[x],y);//將y與x的右子樹合併
	if(dis[ls[x]]<dis[rs[x]])//不滿足左偏了 
	{
		swap(ls[x],rs[x]);
	} 
	dis[x]=dis[rs[x]]+1;//dis[rs[x]]<=dis[ls[x]]
	return x; 
}
int find(int x)
{
	if(rt[x]==x)return x;
	rt[x]=find(rt[x]);
	return rt[x];
}
void work()
{
	dis[0]=-1;
	cin>>n>>m;
	for(int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		t[i]=Tree(i,x);
		rt[i]=i;
	}
	for(int i=1,opt,x,y;i<=m;i++)
	{
		scanf("%d%d",&opt,&x);
		if(opt==1)
		{
			scanf("%d",&y);
			if(del[x]||del[y])continue;
			x=find(x),y=find(y);
			if(x==y)continue;
			rt[x]=rt[y]=merge(x,y);
		}
		else
		{
			if(del[x])
			{
				printf("%d\n",-1);
				continue;
			}	
			x=find(x);
			printf("%d\n",t[x].v);
			del[x]=1;
			rt[ls[x]]=rt[rs[x]]=rt[x]=merge(ls[x],rs[x]);
			ls[x]=rs[x]=dis[x]=0;
		}
	}
}
int main()
{
	freopen("P3377.in","r",stdin);//freopen("P3377.out","w",stdout);
	work();
}

相關文章