[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
",ans);
		else
		{
			scanf("%d%d",&x,&y);
			Merge(col[x],col[y]);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/yexinqwq/p/11167085.html