洛谷P3201 [HNOI2009] 梦幻布丁(链式存储+启发式合并)
链式存储后按题意模拟就行
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3;
inline int read(){
int ret=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-f;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
ret=ret*10+(ch^'0');
ch=getchar();
}
return f*ret;
}
int n,m;
int c[maxn];
int head[maxn];
int nex[maxn];
int fa[maxn];
int sz[maxn];
int st[maxn];
int ans;
int x,xx,y,yy;
void add(int a,int b){
for(int i=head[a];i;i=nex[i]){
ans-=(c[i-1]==b)+(c[i+1]==b);
}
for(int i=head[a];i;i=nex[i]){
c[i]=b;
}
nex[st[a]]=head[b];
head[b]=head[a];
sz[b]+=sz[a];
st[a]=sz[x]=head[a]=0;
return ;
}
int main(){
n=read();
m=read();
for(int i=1;i<=n;i++){
c[i]=read();
ans+=(c[i]!=c[i-1]);
fa[c[i]]=c[i];
/*为什么我们要设置fa数组?
假设有1,2两种颜色,显然将2全变为1和将1全变为2对答案的贡献是同样的我们考虑到对时间复杂度的优化
所以对合并采取的是启发式合并,因此我们会将该颜色布丁个数较小的颜色染成较大的颜色
这时就引出了一个问题,假设存在1,2两种颜色,1颜色的布丁数量小于2,这时我们把2染成1,启发式合并导致我们实际把1染成2,假设下一个操作要对1进行,但我们已经把1变成2了,很明显是无法继续的.
这时我们便需要用到fa数组了,我们把fa数组初始化为每种颜色的编号,当每次启发式合并的合并顺序和当前所给不同时,我们便交换两种颜色的fa数组,我们让fa[1]=2,fa[2]=1这样每次对1,2,操作对fa进行操作。便可以避免出现上述情况。
*/
if(!head[c[i]])
st[c[i]]=i;
sz[c[i]]++;
nex[i]=head[c[i]];
head[c[i]]=i;
}
int q;
for(int i=1;i<=m;i++){
q=read();
if(q==2){
cout<<ans<<endl;
}
else{
x=read();
y=read();
if(fa[x]==fa[y]){
continue;
}
if(sz[fa[x]]>sz[fa[y]]){
swap(fa[x],fa[y]);//这一步交换非常重要。
}
if(!sz[fa[x]])
continue;
add(fa[x],fa[y]);
}
}
return 0;
}