bzoj 1483 梦幻补丁
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。
n,m<=1000000
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
HINT
思路: 本题可以启发式合并,每次把数量少的颜色合并到数量大的颜色,这样的话由于每次都要扩大一倍以上,所以每个数参与合并的次数不会超过logn,总的时间复杂度是nlogn。
先把初始序列的答案统计出来,然后把每种数都用一个链表串起来,修改的时候把两种数的链表合并一下。修改答案的时候,比如把数x全部修改为数y,那么把数x的链表遍历一次,某个数x左边有y就把答案-1,右边有y也-1。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define rep(i,a,b) for(R i=a;i<=b;i++) 5 #define Rep(i,a,b) for(R i=a;i>=b;i--) 6 #define LL long long 7 #define ms(i,a) memset(a,i,sizeof(a)) 8 #define lc (x<<1) 9 #define rc (x<<1|1) 10 #define mid (l+r)/2 11 #define gc() getchar() 12 template<class T>void read(T &x){ 13 x=0; char c=0; 14 while (!isdigit(c)) c=gc(); 15 while (isdigit(c)) x=x*10+(c^48),c=gc(); 16 } 17 int const N=1000000+3; 18 int n,m,a[N],ans,pos[N],size[N],f[N],nt[N],last[N]; 19 int main(){ 20 read(n); read(m); 21 rep(i,1,n){ 22 read(a[i]); 23 if(a[i]!=a[i-1]) ans++ ; 24 pos[a[i]]=a[i]; 25 if(!f[a[i]]) f[a[i]]=i; 26 size[a[i]]++; nt[i]=last[a[i]]; last[a[i]]=i; 27 } 28 rep(i,1,m){ 29 int k; 30 read(k); 31 if(k==1){ 32 int x,y; 33 read(x); read(y); 34 if(x==y) continue; 35 if(size[pos[x]]>size[pos[y]]) swap(pos[x],pos[y]); 36 x=pos[x];y=pos[y]; 37 if(!size[x]) continue; 38 for(int i=last[x];i;i=nt[i]){ 39 if(a[i+1]==y) ans--; 40 if(a[i-1]==y) ans--; 41 } 42 for(int i=last[x];i;i=nt[i]) a[i]=y; 43 size[y]+=size[x]; size[x]=0; 44 nt[f[y]]=last[x]; f[y]=f[x]; 45 }else printf("%d\n",ans); 46 } 47 return 0; 48 }