HNOI2009 夢幻布丁 演算法技巧之鄰接鏈

2022-05-11 01:36:47 字數 1890 閱讀 3596

n個布丁擺成一行,進行m次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.

輸入格式:

第一行給出n,m表示布丁的個數和好友的操作次數. 第二行n個數a1,a2...an表示第i個布丁的顏色從第三行起有m行,對於每個操作,若第乙個數字是1表示要對顏色進行改變,其後的兩個整數x,y表示將所有顏色為x的變為y,x可能等於y. 若第乙個數字為2表示要進行詢問當前有多少段顏色,這時你應該輸出乙個整數. 0

輸出格式:

針對第二類操作即詢問,依次輸出當前有多少段顏色.

輸入樣例#1:

4 3

1 2 2 1

21 2 1

2

輸出樣例#1:

3

1

1<=n,m<=100,000; 0首先上網路上的題解:

1:將兩個佇列合併,有若干佇列,總長度為n,直接合併,最壞o(n),

2:啟發式合併呢?

每次我們把短的合併到長的上面去,o(短的長度)

咋看之下沒有多大區別,

下面讓我們看看均攤的情況:

1:每次o(n)

2:每次合併後,佇列長度一定大於等於原來短的長度的兩倍。

這樣相當於每次合併都會讓短的長度擴大一倍以上,

最多擴大logn次,所以總複雜度o(nlogn),每次o(logn)。

然後對於此題

我們先求出原序列的答案

每一種顏色搞一條鏈把該色結點串起來,記錄下鏈條尾結點

把一種顏色的染成另一種,很簡單把它合併過去,然後處理下對於答案的影響

但是。。。

比如把1染成2,但是s[1]>s[2],這時我們應該將2合併到1的鏈後面,但是會遇到乙個麻煩的問題,就是這個鏈頭是接1下的,也就是說以後找顏色2,發現沒有顏色2只有顏色1。。。

於是我們應該開乙個陣列f,表示我們尋找一種顏色時,實際應該找哪個顏色下的鏈,遇到上面那種情況要交換f[1]和f[2]

詳見以下是個人見解:

直接上**,主要關注**中的注釋。

#include#include

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

const

int maxn=1000001

;int

n,m,ans;

int s[maxn],//

每乙個顏色的個數

next[maxn],//

鄰接鏈

head[maxn],//

鄰接鏈 map[maxn],//

存圖

dp[maxn],//

當前位置的實際顏色

first[maxn];//

某顏色第一次出現的位置

void solve(int a,int

b)//

計算對結果的影響

for(int i=head[a];i!=-1;i=next[i])map[i]=b;//

更改顏色

next[first[a]]=head[b];head[b]=head[a];s[b]+=s[a];

head[a]=first[a]=s[a]=0;//

將兩個鄰接鏈合併,只需更改後繼順序即可

}int

main()

//輸入,賦初值 ,建立鄰接鏈

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

}return0;

}

HNOI2009 夢幻布丁

題意 n個布丁擺成一行,進行m次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.對每個顏色的位置維護鍊錶。合併兩個顏色,連線鍊錶,統計貢獻。統計貢獻的複雜度是與鍊錶長度有關的。如果遍歷長度短的鍊錶那麼複雜度自然更小...

HNOI2009 夢幻布丁

n個布丁擺成一行,進行m次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.輸入格式 第一行給出n,m表示布丁的個數和好友的操作次數.第二行n個數a1,a2.an表示第i個布丁的顏色從第三行起有m行,對於每個操作,若...

HNOI2009 夢幻布丁

題目鏈結 把每一種數字想象成乙個佇列。如果暴力合併,時間複雜度必然很高,考慮啟發式合併 每次把數字少的佇列合併到數字多的佇列上去。每次合併,若數字少的佇列數字個數為 s 則合併之後產生新佇列的大小必定不小於 2 s 時間複雜度 o n log n 這樣合併產生了乙個問題 即原來要求將 a 全部變為 ...