[HNOI2009]梦幻果冻
Description:
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
Hint:
\(n \le 10^5,col \le 10^6\)
Solution:
连这么SB的题都没想到
如果要用线段树的话,不能多区间修改
考虑优化暴力,先对每种颜色用set维护它的所有位置
由于两种颜色1->2和2->1没有本质区别,故考虑启发式合并
每次先看每个点的左右能否对它产生贡献,来更新答案
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
#define sit set<int>::iterator
using namespace std;
typedef long long ll;
const int mxn=1e6+5;
int n,m,ans,cnt,a[mxn],bl[mxn],hd[mxn];
inline int read() {
char c=getchar(); int x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}
struct ed {
int to,nxt;
}t[mxn<<1];
inline void add(int u,int v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
set<int > s[mxn];
void merge(int x,int y) {
for(sit it=s[x].begin();it!=s[x].end();++it) {
int z=*it;
if(a[z+1]==y) --ans;
if(a[z-1]==y) --ans;
s[y].insert(z);
}
for(sit it=s[x].begin();it!=s[x].end();++it) //这里有个细节,需要先check答案然后再修改
a[*it]=y;
s[x].clear();
}
int main()
{
n=read(); m=read(); int opt,x,y;
for(int i=1;i<=n;++i) {
a[i]=read(); bl[a[i]]=a[i];
if(a[i]!=a[i-1]) ++ans;
s[a[i]].insert(i);
}
for(int i=1;i<=m;++i) {
opt=read();
if(opt==2) {
printf("%d\n",ans);
continue ;
}
x=read(); y=read();
if(x==y) continue ;
if(s[bl[x]].size()>s[bl[y]].size()) swap(bl[x],bl[y]);
merge(bl[x],bl[y]);
}
return 0;
}