1. 程式人生 > >P3201 [HNOI2009]夢幻布丁

P3201 [HNOI2009]夢幻布丁

題目描述

N個布丁擺成一行,進行M次操作.每次將某個顏色的布丁全部變成另一種顏色的,然後再詢問當前一共有多少段顏色.例如顏色分別為1,2,2,1的四個布丁一共有3段顏色.

輸入輸出格式

輸入格式:

第一行給出N,M表示布丁的個數和好友的操作次數. 第二行N個數A1,A2...An表示第i個布丁的顏色從第三行起有M行,對於每個操作,若第一個數字是1表示要對顏色進行改變,其後的兩個整數X,Y表示將所有顏色為X的變為Y,X可能等於Y. 若第一個數字為2表示要進行詢問當前有多少段顏色,這時你應該輸出一個整數. 0

輸出格式:

針對第二類操作即詢問,依次輸出當前有多少段顏色.

輸入輸出樣例

輸入樣例#1: 
4 3
1 2 2 1
2
1 2 1
2
輸出樣例#1: 
3
1

說明

1<=n,m<=100,000; 0<Ai,x,y<1,000,000

 

Solution:

  本題平衡樹+啟發式合併。

  考試的時候沒有注意$x==y$的情況,結果只對了8個點,GG。

  思路比較簡單,我們用多棵平衡樹維護每種顏色的下標,在對一種顏色的平衡樹進行操作的同時可以處理出這個顏色的連續段數,具體來說,對於一種顏色,若新加入一個節點,就判斷該節點能否與其前趨或者後繼相接,討論一下各種情況就好了。

  在合併的時候用啟發式的思想把節點少的樹的每個節點暴力加入節點多的樹中,若$size$少的樹是變換後的顏色所在樹,直接交換兩種顏色的樹根就可以了。

  查詢就直接輸出全域性的答案。

  時間複雜度:$O(n\log ^2 n)$

  (坑點:$x==y$時不需要操作)

程式碼:

/*Code by 520 -- 10.25*/
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define
Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) #define debug printf("%s %d\n",__FUNCTION__,__LINE__) using namespace std; const int N=1e6+5; int n,m,root[N],ans,sum[N]; int col[N],ch[N][2],cnt,fa[N],rnd[N],siz[N],date[N]; int gi(){ int a=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar(); return a; } il int newnode(int v){ ++cnt; date[cnt]=v,rnd[cnt]=rand(),siz[cnt]=1,fa[cnt]=0; return cnt; } il void up(int rt){ if(ch[rt][0]) fa[ch[rt][0]]=rt; if(ch[rt][1]) fa[ch[rt][1]]=rt; siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1; } int merge(int x,int y){ if(!x||!y) return x+y; if(rnd[x]<rnd[y]) {ch[x][1]=merge(ch[x][1],y),up(x);return x;} else {ch[y][0]=merge(x,ch[y][0]),up(y);return y;} } void split(int rt,int v,int &x,int &y){ if(!rt) {x=y=0;return ;} if(date[rt]>v) y=rt,split(ch[rt][0],v,x,ch[y][0]),up(y); else x=rt,split(ch[rt][1],v,ch[x][1],y),up(x); } il int kth(int rt,int v){ while(1){ if(siz[ch[rt][0]]>=v) rt=ch[rt][0]; else if(siz[ch[rt][0]]+1<v) v-=siz[ch[rt][0]]+1,rt=ch[rt][1]; else return rt; } } il int pre(int id,int v){ int x,y,ans; split(root[id],date[v]-1,x,y); ans=(!siz[x])?-1:(kth(x,siz[x])); root[id]=merge(x,y),fa[root[id]]=0; return ans; } il int suc(int id,int v){ int x,y,ans; split(root[id],date[v],x,y); ans=(!siz[y])?-1:(kth(y,1)); root[id]=merge(x,y),fa[root[id]]=0; return ans; } il void ins(int id,int v){ int x=pre(id,v),y=suc(id,v); if(x>0&&y>0&&date[x]==date[v]-1&&date[y]==date[v]+1) ans--,sum[id]--; else if(x<0&&y<0||x>0&&date[x]!=date[v]-1&&(y<0||date[y]!=date[v]+1)||y>0&&date[y]!=date[v]+1&&(x<0||date[x]!=date[v]-1)) ans++,sum[id]++; x=y=0; split(root[id],date[v]-1,x,y),root[id]=merge(merge(x,v),y),fa[root[id]]=0; } void dfs(int id,int rt){ if(!rt) return; dfs(id,ch[rt][0]),dfs(id,ch[rt][1]); ch[rt][0]=ch[rt][1]=0; ins(id,rt); } int main(){ n=gi(),m=gi(); int opt,x,y; For(i,1,n) { col[i]=gi(); ins(col[i],newnode(i)); } while(m--){ opt=gi(); if(opt==1) { x=gi(),y=gi(); if(x==y) continue; else if(siz[root[x]]<=siz[root[y]]){ dfs(y,root[x]),root[x]=0;ans-=sum[x],sum[x]=0; } else { swap(root[x],root[y]),swap(sum[x],sum[y]); dfs(y,root[x]),root[x]=0;ans-=sum[x],sum[x]=0; } } else printf("%d\n",ans); } return 0; }