[HNOI2009]梦幻布丁

洛谷题目链接

我们对每一个颜色开一个队列,一开始的答案很显然可以\(O(n)\)得到,那么我们每改一个地方从\(x\)变为\(y\)的话,看一看这个位置前后是不是为\(y\),如果是的话,初始答案\(-1\)

并且,每一次修改相当于合并两个队列,那就想到启发式合并了,那么其实把\(x\)变成\(y\),把\(y\)变成\(x\)其实是一样的,只不过对后面的修改操作有影响

那么我们用\(col[i]\)数组表示现在是\(i\)这个颜色的链表原来是什么颜色

接下来是美滋滋的代码时间~~~

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1500007
using namespace std;
int n,m,ans;
int a[N],col[N],siz[N],head[N],nxt[N];
void Merge(int &x,int &y)
{
	if(siz[x]>siz[y])
		swap(x,y);
	if(!siz[x]||x==y)
		return;
	for(int i=head[x];i!=-1;i=nxt[i])
		ans-=(a[i-1]==y)+(a[i+1]==y);
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		a[i]=y;
		if(nxt[i]==-1)
		{
			nxt[i]=head[y];
			head[y]=head[x];
			break;
		}
	}
	siz[y]+=siz[x];
	siz[x]=0;
	head[x]=0;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=N-7;++i)
		head[i]=-1,col[i]=i;
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		nxt[i]=head[a[i]];
		head[a[i]]=i;
		++siz[a[i]];
		if(a[i-1]!=a[i])
			++ans;
	}
	for(int i=1;i<=m;++i)
	{
		int opt,x,y;
		scanf("%d",&opt);
		if(opt==2)
			printf("%d\n",ans);
		else
		{
			scanf("%d%d",&x,&y);
			Merge(col[x],col[y]);
		}
	}
	return 0;
}
posted @ 2019-07-10 22:36  模拟退火  阅读(133)  评论(0)    收藏  举报