HYSBZ 1483 梦幻布丁
模拟链表,链表启发式合并
题意
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
思路
暴力建出布丁的链表,记录每种颜色的起始位置,结束位置。
合并时将一个颜色的链表遍历,全改成另一种颜色,统计对ans的影响。并将链表连起来。
为了防止退化,统计出每个颜色的size,进行启发式合并。nc数组是记录某颜色实际的位置用的。
所以对于这道题,我们先将初始的答案处理出来,考虑将一个颜色全部暴力修改为另一种颜色之后对答案的影响,就是如果这个位置左面或者右面和目标颜色相同,ans- -。
但是由于我们要启发式合并,有可能交换两个颜色,这里有个小问题需要处理,如果我们要将颜色1变为颜色2,但是由于启发式合并颜色2并到了颜色1上,以后找颜色2时我们应该找颜色1,怎么办呢…
处理办法就是用一个nc[i]数组(nowcolor…)记录i颜色当前是是什么颜色,遇到要交换的情况时就swap(nc[a],nc[b]);
代码
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=2000007;
int c[MAXN], sz[MAXN], nc[MAXN];
int he[MAXN], ta[MAXN], ne[MAXN];
int ans;
void merge(int a, int b)
{
if(sz[a]==0) return;
for(int i=he[a];~i;i=ne[i])
{
if(c[i-1]==b) ans--;
if(c[i+1]==b) ans--;
}
for(int i=he[a];~i;i=ne[i])
{
c[i]=b;
}
ne[ta[b]]=he[a];ta[b]=ta[a];
sz[b]+=sz[a];sz[a]=0;
he[a]=ta[a]=-1;
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m)==2)
{
M(he, -1), M(ta, -1), M(ne, -1);M(sz, 0);
ans=0;
for(int i=1;i<=n;i++)
{
int tmp;
scanf("%d", &tmp);
c[i]=tmp;
if(ta[tmp]==-1) ta[tmp]=i;
ne[i]=he[tmp];
he[tmp]=i;
if(c[i]!=c[i-1]) ans++;
nc[tmp]=tmp;
sz[tmp]++;
}
for(int i=1;i<=m;i++)
{
int op;scanf("%d", &op);
if(op==1)
{
int c1, c2;scanf("%d%d", &c1, &c2);
if(c1==c2) continue;
else
{
if(sz[nc[c1]]>sz[nc[c2]])
swap(nc[c1], nc[c2]);
merge(nc[c1], nc[c2]);
}
}
else
{
printf("%d\n", ans);
}
}
}
//system("pause");
return 0;
}