题目描述
题解
将同一种颜色建成一个链表,然后每次合并的时候将小的遍历一遍看一看两边的是否和它是同一个颜色,动态维护答案,然后将小的直接接到大的后面就可以了。这样的话因为每次都是将小的合并到大的里面去,那么每次至少放大到两倍,那么对于每一个点来说最多被合并logn次,均摊复杂度
O(nlogn)
。
不过这里有一个坑点:每一次只能将小的遍历一遍,也就是说只能修改小的的颜色。但是题目不一定要修改小的的颜色,我刚开始只是保证了两个链表是一个颜色的,但是往后查询的时候就乱套了。所以要加一个数组f[i],类似于i的代表元素,也就是说颜色i相当于f[i]这种颜色。当因为链表的大小要交换的时也要将f[x]和f[y]交换。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 100005
#define C 1000005
int n,m,Max,opt,x,y,color,ans;
int a[N],nxt[N],head[C],tail[C],cnt[C],f[C];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]); Max=max(Max,a[i]);
if (a[i]!=a[i-1]) ans++;
if (tail[a[i]]==0) head[a[i]]=i;
nxt[i]=tail[a[i]];
tail[a[i]]=i;
cnt[a[i]]++;
}
for (int i=1;i<=Max;++i) f[i]=i;
for (int i=1;i<=m;++i)
{
scanf("%d",&opt);
if (opt==1)
{
scanf("%d%d",&x,&y);
if (f[x]==f[y]) continue;
if (cnt[f[x]]>cnt[f[y]]) swap(f[x],f[y]);
x=f[x],y=f[y];
if (cnt[x]==0) continue;
color=y;
for (int now=tail[x];now;now=nxt[now])
{
if (a[now-1]==color) ans--;
if (a[now+1]==color) ans--;
}
for (int now=tail[x];now;now=nxt[now]) a[now]=color;
nxt[head[x]]=tail[y],tail[y]=tail[x],cnt[y]+=cnt[x];
head[x]=tail[x]=cnt[x]=0;
}
else printf("%d\n",ans);
}
}