日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

【bzoj1486】【[HNOI2009]梦幻布丁】启发式链表合并(详解)

發(fā)布時間:2025/3/15 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【bzoj1486】【[HNOI2009]梦幻布丁】启发式链表合并(详解) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


(畫師當(dāng)然是武內(nèi)崇啦)

Description
N個布丁擺成一行,進(jìn)行M次操作.每次將某個顏色的布丁全部變成另一種顏色的,然后再詢問當(dāng)前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.
Input
第一行給出N,M表示布丁的個數(shù)和好友的操作次數(shù). 第二行N個數(shù)A1,A2…An表示第i個布丁的顏色從第三行起有M行,對于每個操作,若第一個數(shù)字是1表示要對顏色進(jìn)行改變,其后的兩個整數(shù)X,Y表示將所有顏色為X的變?yōu)閅,X可能等于Y. 若第一個數(shù)字為2表示要進(jìn)行詢問當(dāng)前有多少段顏色,這時你應(yīng)該輸出一個整數(shù). 0
Output
針對第二類操作即詢問,依次輸出當(dāng)前有多少段顏色.
Sample Input
4 3
1 2 2 1
2
1 2 1
2
Sample Output
3
1

最近在復(fù)習(xí)數(shù)據(jù)結(jié)構(gòu),本來打算找一道平衡樹的題來做,在黃學(xué)長的博客里看到這道題。結(jié)果發(fā)現(xiàn)和平衡樹其實沒有關(guān)系。。。

看到這個題的第一想法是暴力:每次o(n)修改或查詢
然而o(n^2)肯定會爆(雖然題目不給范圍神坑)。我們希望能夠通過某些手段來降log。首先是想到線段樹,因為線段樹可以解決區(qū)間內(nèi)連續(xù)色段,但是我們發(fā)現(xiàn)這道題是針對整個序列而言,且修改無法用線段樹優(yōu)化。

那怎么辦呢?我們發(fā)現(xiàn)這道題是將所有顏色為x的改為y,總共有效的修改數(shù)量是初始時的顏色種數(shù)(最多n)。其實這相當(dāng)于將顏色x與顏色y合并,且之后不會再拆開。所以說這就是合并的問題啦~(廢話了這么久。。)

但是該如何合并呢?我們將同一種顏色的布丁用鏈表連起來,合并的時候是o(1)的。但是對于合并時ans的更新是o(n)的(對于每一個都判斷修改后是否與左右連接)。總的來說,就是每次合并時的復(fù)雜度“被修改的顏色的布丁個數(shù)”。

這個是可以優(yōu)化的,就是用啟發(fā)式合并(把小的往大的合并)。這樣就是o(nlogn)的了。證明就搬一下黃學(xué)長的;

1:每次O(N)
2:每次合并后,隊列長度一定大于等于原來短的長度的兩倍。
這樣相當(dāng)于每次合并都會讓短的長度擴(kuò)大一倍以上,
最多擴(kuò)大logN次,所以總復(fù)雜度O(NlogN),每次O(logN)。

但是由于為了啟發(fā)式合并,我們改變了合并方向。需要用一個f[i]數(shù)組來存 調(diào)用i顏色時真正用到的顏色。

下面談?wù)勬湵?#xff1a;
我以前一直都不清楚鏈表到底是個什么貨。現(xiàn)在好像是明白了:
有兩種鏈表:
1、對于每個點,有一個pre(前繼)和nxt(后繼)。這相當(dāng)于雙向鏈表
2、記錄一個鏈表的開頭head,對每個點記錄一個nxt(下一個)。這相當(dāng)于是單向鏈表

這道題需要訪問鏈表的全部元素,所以用第二種鏈表。

(其實之前接觸過這鏈表很多次,但一直不知道這就是鏈表)

放代碼啦:

#include<cstdio> #include<cstring> #include<algorithm> using namespace std;const int N=1000000+5;int n,m,c[N],siz[N],f[N],ans=0; int head[N],nxt[N],st[N];void solve(int a,int b){for(int i=head[a];i;i=nxt[i]){if(c[i+1]==b) ans--;if(c[i-1]==b) ans--;}for(int i=head[a];i;i=nxt[i]) c[i]=b;/*這是兩種不同的合并方式*/ // nxt[st[a]]=head[b];head[b]=head[a];nxt[st[b]]=head[a]; // head[a]=0,st[a]=0;st[b]=st[a]; } int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&c[i]);siz[c[i]]++,f[c[i]]=c[i];if(c[i]!=c[i-1]) ans++;if(!head[c[i]]) st[c[i]]=i;nxt[i]=head[c[i]],head[c[i]]=i;}int opt,x,y;while(m--){scanf("%d",&opt);if(opt==2) printf("%d\n",ans);else{scanf("%d%d",&x,&y);if(x==y) continue;//if(siz[f[x]]==0) continue;if(siz[f[x]]>siz[f[y]]) swap(f[x],f[y]);if(siz[f[x]]==0) continue;siz[f[x]]+=siz[f[y]],siz[f[x]]=0;//+=solve(f[x],f[y]);}}return 0; }

轉(zhuǎn)載于:https://www.cnblogs.com/LinnBlanc/p/7763119.html

總結(jié)

以上是生活随笔為你收集整理的【bzoj1486】【[HNOI2009]梦幻布丁】启发式链表合并(详解)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。