题解 P3201 【[HNOI2009]梦幻布丁】
如果没有修改,那么直接扫一遍,看有多少个位置满足
有了修改,那么我们就考虑暴力修改
如果要把颜色
然后我们发现把
复杂度看起来就是个暴力,但是注意每次合并至少会把一个颜色的规模翻倍,所以每个点最多被处理
即启发式合并.
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1500000;
int ans[N],size[N],tmp[N],fst[N],nxt[N],f[N],s,n,m,a[N];
void debug(){cout<<233<<endl;}
void merge(int &x,int &y)
{
if(size[x]>size[y])swap(x,y);//交换
if(!size[x]||x==y)return;
for(int i=fst[x];i!=-1;i=nxt[i])
s-=(a[i-1]==y)+(a[i+1]==y);//更新答案
for(int i=fst[x];i!=-1;i=nxt[i])
{
a[i]=y;//修改
if(nxt[i]==-1){nxt[i]=fst[y],fst[y]=fst[x];break;}//合并
}
size[y]+=size[x],size[x]=0,fst[x]=-1;//合并
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<N;i++)fst[i]=-1,f[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);int x=a[i];
nxt[i]=fst[x],fst[x]=i,size[x]++;//前向星
if(nxt[i]!=i-1)s++;
}
for(int i=1;i<=m;i++)
{
int opt,x,y;
scanf("%d",&opt);
if(opt==2)printf("%d\n",s);
else scanf("%d%d",&x,&y),merge(f[x],f[y]);
}
}