【bzoj1483】[HNOI2009]梦幻布丁 set
【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
1 2 2 1
2
1 2 1
2
Sample Output
3
1
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 }