【金牌导航】【洛谷 P3201】【启发式合并】梦幻布丁
题目
解题思路
启发式合并真的就是想到挺难的,想到后简单得很
如何判断有几段还是很容易的,如果和前一个布丁颜色不同,段数+1
将每一种颜色看作一条链
改变颜色可以看作将两条链合并
为了时间更优,肯定是要将数量少的合并到多的
所以要记录原来的颜色x当前是颜色b[x]
如果改颜色后与前面或后面颜色相同,段数-1
合并后要及时更新颜色的数量
代码
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,q,x,y,ans,a[100010],b[1000010],f[100010];
int cnt[1000010],head[1000010];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[a[i]]=a[i]; //当前颜色没变
cnt[a[i]]++;
f[i]=head[a[i]]; //上一个同颜色的
head[a[i]]=i; //此颜色最后一个的位置
if (a[i]!=a[i-1]) ans++;
}
while (m--)
{
scanf("%d",&q);
if (q==2)
{
printf("%d\n",ans);
continue;
}
int j=0;
scanf("%d%d",&x,&y);
if (x==y) continue;
if (cnt[b[x]]>cnt[b[y]]) swap(b[x],b[y]); //将数量少的合并到多的,改变颜色
x=b[x],y=b[y];
for (int i=head[x];i;i=f[i])
{
if (a[i-1]==y) ans--;
if (a[i+1]==y) ans--;
}
for (int i=head[x];i;i=f[i]) a[j=i]=y;
if (head[x])
{
f[j]=head[y],head[y]=head[x];
cnt[y]+=cnt[x],head[x]=cnt[x]=0; //合并后更新数量,改变链头
}
}
return 0;
}