[SDOI2011]染色

[SDOI2011]染色

describes
給定一棵有n個節點的無根樹和m個操作,操作有2類:
1、將節點a到節點b路徑上所有點都染成顏色c;
2、詢問節點a到節點b路徑上的顏色段數量(連續相同顏色被認爲是同一段),如“112221”由3段組成:“11”、“222”和“1”。
請你寫一個程序依次完成這m個操作。

Input
第一行包含2個整數n和m,分別表示節點數和操作數;
第二行包含n個正整數表示n個節點的初始顏色
下面 行每行包含兩個整數x和y,表示x和y之間有一條無向邊。
下面 行每行描述一個操作:
“C a b c”表示這是一個染色操作,把節點a到節點b路徑上所有點(包括a和b)都染成顏色c;
“Q a b”表示這是一個詢問操作,詢問節點a到節點b(包括a和b)路徑上的顏色段數量。

Output
對於每個詢問操作,輸出一行答案。

Sample Input
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output
3
1
2

HINT

Time Limit: 20 Sec Memory Limit: 512 MB
數N<=10^5,操作數M<=10^5,所有的顏色C爲整數且在[0, 10^9]之間。

題解:

題目初看很容易理解,就知道是線段樹經典題目和樹鏈剖分的結合,用線段樹維護區間的sum,left color,right color,delta即可。update,和pushdown和線段樹裏的經典題一樣不用說了。
但是很容易發現的問題就是由於線段樹維護的是鏈剖之後的樹,也就是說查詢的區間可能不會相連。
這時可以發現可以通過原樹中的top[x],和fa[top[x]]將查詢的鏈聯繫起來。因爲如果換重鏈,相鄰的兩個節點一定是top[x]和fa[top[x]],接下來就想在線段樹上維護顏色一樣維護就行了。
即:

代碼:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int max_n = 1e5+5;

struct node{
	int l,r;
	int lc,rc,sum,delta;
}tree[max_n*4];

int fa[max_n],deep[max_n],nxt[max_n*2],point[max_n*2],v[max_n*2],val[max_n],top[max_n],size[max_n],rank_n[max_n];
char c;
int n,m,tot,x,y,k,last,cha;

inline void clear()
{
	memset(point,-1,sizeof(point));
	memset(nxt,-1,sizeof(nxt));
	tot=0;
}

inline void addedge(int x,int y)
{
	++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline void update(int now)
{
	tree[now].sum=tree[now<<1].sum+tree[(now<<1)+1].sum;
	tree[now].lc=tree[now<<1].lc;
	tree[now].rc=tree[(now<<1)+1].rc;
	
	if(tree[now<<1].rc==tree[(now<<1)+1].lc)
	  tree[now].sum-=1;
}

inline void pushdown(int now)
{
	if(tree[now].delta!=0)
	{
		tree[now<<1].sum=1;
		tree[now<<1].delta=tree[now].delta;
		tree[now<<1].lc=tree[now<<1].rc=tree[now].delta;
		tree[(now<<1)+1].sum=1;
		tree[(now<<1)+1].delta=tree[now].delta;
		tree[(now<<1)+1].lc=tree[(now<<1)+1].rc=tree[now].delta;
		tree[now].delta=0;
	}
}

inline void build(int now,int l,int r)
{
	tree[now].l=l;
	tree[now].r=r;
	
	if(l==r) return;
	
	int mid=(l+r)>>1;
	build(now<<1,l,mid);
	build((now<<1)+1,mid+1,r);
}

inline void change(int now,int l,int r,int val)
{
	int l1=tree[now].l;
	int r1=tree[now].r;
	
	if(l1>=l && r1<=r)
	{
		tree[now].sum=1;
		tree[now].delta=val;
		tree[now].lc=tree[now].rc=val;
	    return;
	}
	
	pushdown(now);
	int mid=(l1+r1)>>1;
	if(l<=mid)
	  change(now<<1,l,r,val);
	if(r>mid)
	  change((now<<1)+1,l,r,val);
	update(now);
}

inline void schange(int x,int y,int val)
{
	while(top[x]!=top[y])
	{
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		change(1,rank_n[top[x]],rank_n[x],val);
		x=fa[top[x]];
	}
	
	if(rank_n[x]>rank_n[y]) swap(x,y);
	change(1,rank_n[x],rank_n[y],val);
}

inline void dfs1(int now,int f)
{
	size[now]=1;
	deep[now]=deep[f]+1;
	fa[now]=f;
	
	for(int i=point[now]; i!=-1; i=nxt[i])
	  if(v[i]!=f)
	  {
	  	dfs1(v[i],now);
	  	size[now]+=size[v[i]];
	  }
}

inline void dfs2(int now,int tip)
{
	top[now]=tip;
	rank_n[now]=++tot;
	change(1,tot,tot,val[now]);
	
	if(now!=1 && nxt[point[now]]==-1)
	  return;
	
	int mson=0;
	for(int i=point[now]; i!=-1; i=nxt[i])
	  if(size[v[i]]<size[now] && size[mson]<size[v[i]]) mson=v[i];
	    
	dfs2(mson,tip);
	
	for(int i=point[now]; i!=-1; i=nxt[i])
	  if(size[v[i]]<size[now] && v[i]!=mson)
	    dfs2(v[i],v[i]);
	
}

inline int query(int now,int l,int r)
{
	int l1=tree[now].l;int r1=tree[now].r;
	
	if(l1>=l && r1<=r) return tree[now].sum;
	
	pushdown(now);
	int ans=0,mid=(l1+r1)>>1;
	if(l<=mid)
	  ans+=query(now<<1,l,r);
	if(r>mid)
	  ans+=query((now<<1)+1,l,r);
	if(l<=mid && r>mid)
	  if(tree[now<<1].rc==tree[(now<<1)+1].lc) ans--;//判斷相鄰兩個色段交匯處有無同樣的顏色 
	return ans;
}

inline int query_c(int now,int tar)//查詢顏色 
{
	int l=tree[now].l;
	int r=tree[now].r;
	
	if(l==r && l==tar) return tree[now].lc;
	
	pushdown(now);
	int mid=(l+r)/2;
	if(tar<=mid)
	  return query_c(now<<1,tar);
	else 
	  return query_c((now<<1)+1,tar);
}

inline int squery(int x,int y)
{
	int ans=0;
	
	while(top[x]!=top[y])
	{
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		ans+=query(1,rank_n[top[x]],rank_n[x]);
		if(query_c(1,rank_n[fa[top[x]]])==query_c(1,rank_n[top[x]])) ans-=1;//和線段樹不同點:換重鏈是判斷相鄰重鏈是否顏色一致 
 		x=fa[top[x]];
	}
	
	if(rank_n[x]>rank_n[y]) swap(x,y);
	ans+=query(1,rank_n[x],rank_n[y]);
	return ans;
}

int main()
{
	clear();
	scanf("%d%d",&n,&m);
	
	for(int i=1; i<=n; ++i)
	  scanf("%d",&val[i]);
	  
	for(int i=1; i<=n-1; ++i)
	{
		scanf("%d%d",&x,&y);
		addedge(x,y);
	}
	
	tot=0;
	build(1,1,n);
	dfs1(1,0);
	dfs2(1,1);
	
	for(int i=1; i<=m; ++i)
	{
		cin>>c;
		
		if(c=='Q')
		{
			scanf("%d%d",&x,&y);
			printf("%d\n",squery(x,y));
		}
		else
		{
			scanf("%d%d%d",&x,&y,&k);
			schange(x,y,k);
		}
	}
	
	return 0;	
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章