1. 程式人生 > >【bzoj1483】[HNOI2009]夢幻布丁 set

【bzoj1483】[HNOI2009]夢幻布丁 set

con 啟發式合並 har 所有 sin 應該 -a size ||

【bzoj1483】[HNOI2009]夢幻布丁

Description

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

Input

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

Output

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

Sample Input

4 3
1 2 2 1
2
1 2 1
2

Sample Output

3
1 題解:   c++stl    1:將兩個隊列合並,有若幹隊列,總長度為n,直接合並,最壞O(N), 2:啟發式合並呢? 每次我們把短的合並到長的上面去,O(短的長度) 咋看之下沒有多大區別, 下面讓我們看看均攤的情況: 1:每次O(N) 2:每次合並後,隊列長度一定大於等於原來短的長度的兩倍。 這樣相當於每次合並都會讓短的長度擴大一倍以上, 最多擴大logN次,所以總復雜度O(NlogN),每次O(logN)。
 1
#include<cstring> 2 #include<cmath> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdio> 6 #include<set> 7 8 #define N 1000007 9 #define ll long long 10 using namespace std; 11 inline int read() 12 { 13 int x=0,f=1;char ch=getchar(); 14
while(ch>9||ch<0){if (ch==-) f=-1;ch=getchar();} 15 while(ch<=9&&ch>=0){x=(x<<3)+(x<<1)+ch-0;ch=getchar();} 16 return x*f; 17 } 18 19 int n,m,ans; 20 int fa[N],v[N]; 21 set<int>t[N]; 22 23 void solve(int a,int b) 24 { 25 for(set<int>::iterator i=t[a].begin();i!=t[a].end();i++) 26 { 27 if(v[*i-1]==b)ans--; 28 if(v[*i+1]==b)ans--; 29 t[b].insert(*i); 30 } 31 for(set<int>::iterator i=t[a].begin();i!=t[a].end();i++)v[*i]=b; 32 t[a].clear(); 33 } 34 int main() 35 { 36 n=read();m=read(); 37 for(int i=1;i<=n;i++)v[i]=read(); 38 for(int i=1;i<=n;i++) 39 { 40 fa[v[i]]=v[i]; 41 if(v[i]!=v[i-1])ans++; 42 t[v[i]].insert(i); 43 } 44 for(int i=1;i<=m;i++) 45 { 46 int f=read(),a,b; 47 if(f==2)printf("%d\n",ans); 48 else 49 { 50 a=read();b=read(); 51 if(a==b)continue; 52 if(t[fa[a]].size()>t[fa[b]].size()) 53 swap(fa[a],fa[b]); 54 a=fa[a];b=fa[b]; 55 solve(a,b); 56 } 57 } 58 }

【bzoj1483】[HNOI2009]夢幻布丁 set