题解 P3201 【[HNOI2009]梦幻布丁】
题目链接:BZOJ 1483
有
- 将颜色为
x 的布丁全部变成颜色y 的布丁。 - 询问当前一共有多少段颜色(例如颜色分别为
1,2,2,1 的4 个布丁一共有3 段颜色)。
数据范围:
Solution
我们可以把每种颜色的布丁集合想象成是一个队列。那么就有若干个长度总和为
但是有个叫做启发式合并的东西!它的本质很简单:每次把短的合并到长的上面,那么合并一次的复杂度为
考虑用贡献法来分析。我们令两个集合的分别为
对于这道题目,我们先求出原序列的答案,对于每一种颜色都用类似链表的数据结构串起来,并记录下尾节点。每次修改,都根据启发式合并的方法来暴力合并,然后处理一下此次合并对答案的影响(显然答案是不增的)。
但是如果我们把
时间复杂度:
Code
#include <iostream>
#include <cstdio>
const int N=1e5+5,M=1e6+5;
int n,m,c[N],sz[M],st[M],f[M],hd[M],nxt[N],ans;
void merge(int x,int y) {
for(int i=hd[x];i;i=nxt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
for(int i=hd[x];i;i=nxt[i]) c[i]=y;
nxt[st[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x];
hd[x]=st[x]=sz[x]=0;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
scanf("%d",&c[i]),f[c[i]]=c[i];
ans+=c[i]!=c[i-1];
if(!hd[c[i]]) st[c[i]]=i;
++sz[c[i]],nxt[i]=hd[c[i]],hd[c[i]]=i;
}
while(m--) {
int opt;
scanf("%d",&opt);
if(opt==2) printf("%d\n",ans);
else {
int x,y;
scanf("%d%d",&x,&y);
if(x==y) continue;
if(sz[f[x]]>sz[f[y]]) std::swap(f[x],f[y]);
if(!sz[f[x]]) continue;
merge(f[x],f[y]);
}
}
return 0;
}