1. 程式人生 > >【模板】左偏樹(可並堆)

【模板】左偏樹(可並堆)

inline 限制 需要 表示 開始 cnblogs -a 刪除 ont

題目描述

如題,一開始有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所得的結果。

輸入輸出樣例

輸入樣例#1:
5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
輸出樣例#1:
1
2

說明

當堆裏有多個最小值時,優先刪除原序列的靠前的,否則會影響後續操作1導致WA。

時空限制:1000ms,128M

數據規模:

對於30%的數據:N<=10,M<=10

對於70%的數據:N<=1000,M<=1000

對於100%的數據:N<=100000,M<=100000

樣例說明:

初始狀態下,五個小根堆分別為:{1}、{5}、{4}、{2}、{3}。

第一次操作,將第1個數所在的小根堆與第5個數所在的小根堆合並,故變為四個小根堆:{1,3}、{5}、{4}、{2}。

第二次操作,將第2個數所在的小根堆與第5個數所在的小根堆合並,故變為三個小根堆:{1,3,5}、{4}、{2}。

第三次操作,將第2個數所在的小根堆的最小值輸出並刪除,故輸出1,第一個數被刪除,三個小根堆為:{3,5}、{4}、{2}。

第四次操作,將第4個數所在的小根堆與第2個數所在的小根堆合並,故變為兩個小根堆:{2,3,5}、{4}。

第五次操作,將第2個數所在的小根堆的最小值輸出並刪除,故輸出2,第四個數被刪除,兩個小根堆為:{3,5}、{4}。

故輸出依次為1、2。

思路

左偏樹板子題;

代碼實現

 1 #include<cstdio>
 2 const int maxn=1e6+10;
 3 int n,m;
 4 int f[maxn],s[maxn]={-1},d[maxn],l[maxn],r[maxn];
 5 inline void swap_(int&x,int&y){x^=y,y^=x,x^=y;}
 6 int ff(int x){return f[x]==x?x:f[x]=ff(f[x]);}
 7 int merger(int a,int b){
 8     if(!a) return b;
 9     if(!b) return a;
10     if(s[a]>s[b]) swap_(a,b);
11     r[a]=merger(r[a],b);
12     if(d[r[a]]>d[l[a]]) swap_(l[a],r[a]);
13     d[a]=d[r[a]]+1;
14     return a;
15 }
16 int a,b,c;
17 int main(){
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;i++) scanf("%d",&s[i]),f[i]=i;
20     while(m--){
21         scanf("%d",&c);
22         if(c==1){
23             scanf("%d%d",&a,&b);
24             if(s[a]==-1||s[b]==-1) continue;
25             a=ff(a),b=ff(b);
26             if(a!=b)
27             f[a]=f[b]=merger(a,b);
28         }
29         if(c==2){
30             scanf("%d",&a);
31             if(s[a]!=-1){
32                 a=ff(a);
33                 printf("%d\n",s[a]);s[a]=-1;
34                 f[a]=merger(l[a],r[a]);
35                 f[f[a]]=f[a];
36             }
37             else puts("-1");
38         }
39     }
40     return 0;
41 }

【模板】左偏樹(可並堆)