洛谷 P3377 模板 左偏樹(可並堆)

2022-05-26 09:06:12 字數 4036 閱讀 1805

2019-06-01

題目: 洛谷 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所得的結果。

輸入樣例#1:

5 5

1 5 4 2 3

1 1 5

1 2 5

2 21 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個數所在的小根堆合併,故變為四個小根堆:、、、。

第二次操作,將第2個數所在的小根堆與第5個數所在的小根堆合併,故變為三個小根堆:、、。

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

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

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

故輸出依次為1、2。

首先,這道題目標題便很明確地告訴我們——這是一道模板題。

是模板題,就應該從理論出發,普及常識,最後得到一長串**(瘋狂背誦)

我在這裡介紹的是「pbds」(平板電視)的解法?

不知道 pbds的同學請戳這裡,這裡,還有這裡。

那麼顯而易見,這道題一下子沒有了難度。或許有些童鞋已經參照 pbds的姿勢做出來了。

1

//2 #include 3 #include 4

using

namespace

std;

5using

namespace

__gnu_pbds;

6 typedef unsigned long

long

ll;7

#define ri register ll89

structa10

19return x>p.x;

20//

*/21}22

};23

24 __gnu_pbds::priority_queue,pairing_heap_tag> q[100005

];25

ll n,m;

26 ll father[100005

];27

bool vis[100005

];28

29ll findf(ll a)

3036 father[a]=k;

37return

k;38}39

40void

joinf(ll x,ll y)

4146

47signed main()

48);

55 father[i]=i;56}

5758

for(ri i=1;i<=m;i++)

5970

else

if(rerinput==2)71

78 ri wh=findf(k);

79 cout

;80 vis[q[wh].top().rank]=1;81

q[wh].pop();82}

83}8485

//*/

86return0;

87}88//

然後開森的得到下面的結果:

好好好。

我的錯,我的錯。

一定又是**讓我給遺漏了。

那再重審一次題目:我發現我遺漏了操作一後面小括號裡的內容。

於是重新改一次,增加了66,67行的內容

1

//2 #include 3 #include 4

using

namespace

std;

5using

namespace

__gnu_pbds;

6 typedef unsigned long

long

ll;7

#define ri register ll89

structa10

19return x>p.x;

20//

*/21}22

};23

24 __gnu_pbds::priority_queue,pairing_heap_tag> q[100005

];25

ll n,m;

26 ll father[100005

];27

bool vis[100005

];28

29ll findf(ll a)

3036 father[a]=k;

37return

k;38}39

40void

joinf(ll x,ll y)

4146

47signed main()

48);

55 father[i]=i;56}

5758

for(ri i=1;i<=m;i++)

5972

else

if(rerinput==2)73

80 ri wh=findf(k);

81 cout

;82 vis[q[wh].top().rank]=1;83

q[wh].pop();84}

85}8687

//*/

88return0;

89}90//

然後愉快地過了。↑↑↑↑↑↑↑↑↑↑ac**↑↑↑↑↑↑↑↑↑↑

但是!!!

-----看一看題解。

dalao們已經講得很詳細了(根本沒看懂)。

從茫茫**裡,我發現了乙個重要的一句話——————

——————dalao又做粗了結論性的發言

我一下子豁然開朗,去掉了上面**裡的路徑壓縮

結果:

額~~好吧,我又不知道是為什麼了

但我這個**似乎是需要路徑壓縮滴。

小結:這篇隨筆告訴我們——看題要仔細,題目給出的限制一定要考慮!

除此外再ac**第68行:「if(q[xx].size()>q[yy].size())swap(xx,yy);」這個似乎必須 q[xx].size() < q[yy].size()。

但實話說,我也不是很清楚,

如果有dalao知道,還望指教~~

洛谷P3377 模板 左偏樹(可並堆)

題目描述 如題,一開始有n個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 操作1 1 x y 將第x個數和第y個數所在的小根堆合併 若第x或第y個數已經被刪除或第x和第y個數在用乙個堆內,則無視此操作 操作2 2 x 輸出第x個數所在的堆最小數,並將其刪除 若第x個數已經被刪除,則輸出 ...

洛谷 P3377 模板 左偏樹(可並堆)

有 n nn 個小根堆,每個堆只有乙個數,進行兩種操作 1 x y將第 x,y x,yx,y 個數分別在的小根堆合併 2 x輸出第 x xx 個數所在的的堆的最小數,並將其刪除,有多個則刪除最先輸入的,若第 x xx 個數已刪除,則輸出 1 1 1思路 左偏樹 include include def...

洛谷 P3377 模板 左偏樹(可並堆)

如題,一開始有 n 個小根堆,每個堆包含且僅包含乙個數。接下來需要支援兩種操作 1 x y 將第 x 個數和第 y 個數所在的小根堆合併 若第 x 或第 y 個數已經被刪除或第 x 和第 y 個數在用乙個堆內,則無視此操作 2 x 輸出第 x 個數所在的堆最小數,並將這個最小數刪除 若有多個最小數,...