1483:[HNOI2009]夢幻布丁(連結串列+啟發式合併)
阿新 • • 發佈:2018-11-07
1483:[HNOI2009]夢幻布丁
時間限制: 1000 ms 記憶體限制: 65536 KB
提交數: 2 通過數: 2
題目描述
個布丁擺成一行,進行 次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.
輸入
第一行給出
,
表示布丁的個數和好友的操作次數. 第二行
個數
,
…
表示第
個布丁的顏色從第三行起有
行,對於每個操作,若第一個數字是1表示要對顏色進行改變,其後的兩個整數
,
表示將所有顏色為
的變為
,
可能等於
. 若第一個數字為2表示要進行詢問當前有多少段顏色,這時你應該輸出一個整數. 0
輸出
針對第二類操作即詢問,依次輸出當前有多少段顏色.
輸入樣例
4 3
1 2 2 1
2
1 2 1
2
輸出樣例
3
1
解:
乍一看是非常不可過的,但是確實是一道純暴力題目。
給每種顏色開一個連結串列,合併的時候列舉長度小的,計算一下答案減少了多少,然後把兩個合併。同時開一個數組記錄每種顏色對應的編號即可。
這種小的並大的,合併期望複雜度是
的。rand一下即可。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int fa[1000005],head[1000005],nt[100005],col[100005];
int ans,n,m,x,y,fg;
void add(int op,int id){
nt[id]=head[op];
head[op]=id;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&col[i]);
if(col[i]!=col[i-1]) ans++;
add(col[i],i);
fa[col[i]]=col[i];
}
for(int i=1;i<=m;i++){
scanf("%d",&fg);
if(fg==2){
printf("%d\n",ans);
continue;
}
scanf("%d%d",&x,&y);
if(fa[x]==0||x==y) continue;
if(fa[y]==0){
fa[y]=fa[x];fa[x]=0;
continue;
}
if((rand()&1)==0){
int j;
for(j=head[fa[x]];j!=-1;j=nt[j]){
if(col[j-1]==fa[y]) ans--;
if(col[j+1]==fa[y]) ans--;
}
for(int k=head[fa[x]];k!=-1;k=nt[k]) col[k]=fa[y],j=k;
nt[j]=head[fa[y]];head[fa[y]]=head[fa[x]];head[fa[x]]=-1;
fa[x]=0;
}
else{
int j;
for(j=head[fa[y]];j!=-1;j=nt[j]){
if(col[j-1]==fa[x]) ans--;
if(col[j+1]==fa[x]) ans--;
}
for(int k=head[fa[y]];k!=-1;k=nt[k]) col[k]=fa[x],j=k;
nt[j]=head[fa[x]];head[fa[x]]=head[fa[y]];head[fa[y]]=-1;
fa[y]=fa[x];fa[x]=0;
}
}
}