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;
}