「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<N,M<1000010<N,M<100001
0<Ai,x,y<10000000<A_i,x,y<1000000

分析

可以對種顏色開一棵線段樹,範圍爲1~n,表示在區間中爲改顏色的位置,同時線段樹中記錄區間內的段數,爲左+右,若中間都有,則減1。對於顏色變換,直接線段樹合併即可,注意同時維護答案。

注意,對於根節點不能開1000000的數組存,只能用STL map,數組空間開不下,會爆系統棧,然後發生越界現象,然後就有許多莫名奇妙的錯誤,比如WaTLE等。

代碼

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
const int N=100010,M=1000005;
const int LogN=20;
typedef struct SegmentTree {
	int l,r,ls,rs,len;
}Segment,SegT;
SegT nd[N*LogN];
int tot;
int st[N*LogN],top;
int n,m,ans,app[N];
map<int,int> rt;
void Clear(int x) {
	nd[x].l=nd[x].r=0;
	nd[x].len=nd[x].ls=nd[x].rs=0;
}
int New() {
	if (top) return Clear(st[top]),st[top--];
	else return ++tot;
}
void Pushup(int x) {
	int lc=nd[x].l,rc=nd[x].r;
	nd[x].len=nd[lc].len+nd[rc].len;
	if (nd[lc].rs&&nd[rc].ls) nd[x].len--;
	nd[x].ls=nd[lc].ls;
	nd[x].rs=nd[rc].rs;
}
void Add(int &rt,int l,int r,int x,int v) {
	if (!rt) {
		rt=New();
		nd[rt].l=nd[rt].r=0;
	}
	if (l==r) {
		nd[rt].len+=v;
		nd[rt].ls+=v;
		nd[rt].rs+=v;
		return;
	}
	int mid=(l+r)>>1;
	if (x<=mid) Add(nd[rt].l,l,mid,x,v);
	else Add(nd[rt].r,mid+1,r,x,v);
	Pushup(rt);
}
void Recycle(int &y) {
	st[++top]=y;
	Clear(y);
	y=0;
}
void Merge(int l,int r,int&x,int &y) {//x<-y;
	if (!y) return;
	if (!x) {
		x=New();
		nd[x]=nd[y];
		Recycle(y);
		return;
	}
	if (l==r) {
		nd[x].len=nd[y].len;
		nd[x].ls=nd[y].ls;
		nd[x].rs=nd[y].rs;
		Recycle(y);
		return;
	}
	int mid=(l+r)>>1;
	Merge(l,mid,nd[x].l,nd[y].l);
	Merge(mid+1,r,nd[x].r,nd[y].r);
	Recycle(y);
	Pushup(x);
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) {
		int cl;
		scanf("%d",&cl);
		app[i]=cl;
		Add(rt[cl],1,n,i,1);
	}
	sort(app+1,app+n+1);
	int n_=unique(app+1,app+n+1)-app-1;
	for (int i=1;i<=n_;i++)
		ans+=nd[rt[app[i]]].len;
	for (int i=1;i<=m;i++) {
		int op,x,y;
		scanf("%d",&op);
		if (op==2) printf("%d\n",ans);
		else {
			scanf("%d%d",&x,&y);//x->y;
			if (x==y) continue;
			ans-=nd[rt[x]].len;
			ans-=nd[rt[y]].len;
			Merge(1,n,rt[y],rt[x]);
			ans+=nd[rt[y]].len;
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章