BZOJ 1483: [HNOI2009]梦幻布丁(链表+启发式合并)
http://www.lydsy.com/JudgeOnline/problem.php?id=1483
题意:

思路:
每次修改的话需要把同一种颜色的都修改了,那如果去遍历的话就复杂度比较高,如果用链表把颜色相同的连接起来的话那么修改起来就十分方便了。
但是当两个链表需要合并的时候,修改长度短的那一个相对来说会比较省时,这就是启发式合并。但是使用启发式合并的话需要注意,如果现在有操作1->2,本来是要将所有的1改成2,如果1的链表长度大于2的链表长度的话,启发式合并就会将2合并至1,此时也就变成了将2变成1,所以我们需要一个数组pos来记录所要找的颜色在链表中实际对应的颜色。
那么当修改颜色时怎么动态维护答案呢,我们只需要扫描一遍链表,如果此时是x->y,如果颜色为x的某个数左边为y,那么ans--,右边为y,那么ans--。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn = 1000000+5;
int n, m;
int a[maxn],sz[maxn],nxt[maxn],frt[maxn],pos[maxn];
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
int ans = 0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]!=a[i-1]) ans++;
sz[a[i]]++;
pos[a[i]]=a[i];
nxt[i]=frt[a[i]],frt[a[i]]=i;
}
while(m--)
{
int op;
scanf("%d",&op);
if(op==1)
{
int x,y,num;
scanf("%d%d",&x,&y);
if(x==y) continue;
if(sz[pos[x]]>sz[pos[y]]) swap(pos[x],pos[y]);
x=pos[x],y=pos[y];
if(!sz[x]) continue;
for(int i=frt[x];i;i=nxt[i])
{
if(a[i+1]==y) ans--;
if(a[i-1]==y) ans--;
num = i;
}
for(int i=frt[x];i;i=nxt[i]) a[i]=y;
sz[y]+=sz[x],sz[x]=0;
nxt[num]=frt[y];
frt[y] = frt[x];
frt[x] = 0;
}
else printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号