P3377 【模板】左偏樹/可並堆
【模板】左偏樹/可並堆
題目描述
如題,一開始有 \(n\) 個小根堆,每個堆包含且僅包含一個數。接下來需要支援兩種操作:
-
1 x y
:將第 \(x\) 個數和第 \(y\) 個數所在的小根堆合併(若第 \(x\) 或第 \(y\) 個數已經被刪除或第 \(x\) 和第 \(y\) 個數在同一個堆內,則無視此操作)。 -
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();
}