[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;
}

posted @ 2019-03-22 19:08  cloud_9  阅读(108)  评论(0编辑  收藏  举报