题目大意:维护数列,兹瓷两种操作:1.把所有x变成y
2.询问数列中的连续段数,相邻且相同的为一段
题解:先求一下初始的答案,记作ans,然后维护它
由于没有数据范围,考虑用平衡树启发式合并暴力
用若干个链表维护,一个链表维护一种颜色出现的各个位置
操作1:启发式合并链表,同时维护答案
为了方便在启发式合并时因为size而交换链表,记录p[i]表示颜色i所代表的实际颜色,初始p[i]=i,交换p[x],p[y]即可
操作2:输出ans……
这里的多个链表使用前向星实现……
我终于会链表了
我的收获:求初始答案然后维护似乎挺常见的
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define M 1000100
int n,q,z,ans,t;
int c[M],sz[M],head[M],p[M];
struct edge{int to,nex;}e[M<<1];
void add(int u,int v){e[t]=(edge){v,head[u]};head[u]=t++;}
void merge(int x,int y)
{
if(x==y) return ;//!!!
if(sz[p[x]]>sz[p[y]]) swap(p[x],p[y]);
x=p[x];y=p[y];
if(!sz[x]) return ;//!!!
for(int i=head[x];i!=-1;i=e[i].nex){
if(c[e[i].to-1]==y) ans--;//维护相邻位置对答案的影响
if(c[e[i].to+1]==y) ans--;
}
for(int i=head[x];i!=-1;i=e[i].nex) c[e[i].to]=y;//修改
for(z=head[x];e[z].nex!=-1;z=e[z].nex);//找到链表x
e[z].nex=head[y];head[y]=head[x];head[x]=-1;//因为链表不需要考虑单调性,直接把链表y连到链表x后面
sz[y]+=sz[x];sz[x]=0;
}
void work()
{
int opt,x,y;
while(q--)
{
scanf("%d",&opt);
if(opt==1) scanf("%d%d",&x,&y),merge(x,y);
else printf("%d\n",ans);
}
}
void init()
{
t=0,memset(head,-1,sizeof(head));
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
if(c[i]!=c[i-1]) ans++;
sz[c[i]]++;p[c[i]]=c[i];
add(c[i],i);//用链表记录每个c[i]的位置
}
}
int main()
{
init();
work();
return 0;
}