【bzoj 2243】 染色 【SDOI2011】

Description

给定一棵有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

 

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

这道题是一个树链剖分,用线段树维护区间左端点和右端点的颜色,每次更新和查询时只需要注意左子树的右端点和右子树左端点是否相同即可(注意是求颜色段数不是颜色数),下面时程序(注意数组最好开200000,不然可能会RE):

#include<stdio.h>
#include<set>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=200005;
struct treenode{
	int lc,rc,c,l,r,w;
};
struct Segment_tree{
	int k;
	treenode t[N<<2];
	void clear(){
		k=1;
	}
	void pushdown(int rt){
		if(t[rt].c){
			t[t[rt].lc].c=t[t[rt].rc].c=
			t[t[rt].lc].l=t[t[rt].rc].l=
			t[t[rt].lc].r=t[t[rt].rc].r=t[rt].c;
			t[t[rt].lc].w=t[t[rt].rc].w=1;
			t[rt].c=0;
		}
	}
	void updata(int rt){
		t[rt].l=t[t[rt].lc].l;
		t[rt].r=t[t[rt].rc].r;
		t[rt].w=t[t[rt].lc].w+t[t[rt].rc].w-(t[t[rt].lc].r==t[t[rt].rc].l);
	}
	void build(int rt,int l,int r){
		t[rt].c=0;
		if(l==r){
			t[rt].lc=t[rt].rc=0;
			return;
		}
		int m=l+r>>1;
		build(t[rt].lc=++k,l,m);
		build(t[rt].rc=++k,m+1,r);
	}
	void change(int rt,int l,int r,int a,int b,int &w){
		pushdown(rt);
		if(l==a&&r==b){
			t[rt].l=t[rt].r=t[rt].c=w;
			t[rt].w=1;
			return;
		}
		int m=l+r>>1;
		if(b<=m){
			change(t[rt].lc,l,m,a,b,w);
		}
		else{
			if(a>m){
				change(t[rt].rc,m+1,r,a,b,w);
			}
			else{
				change(t[rt].lc,l,m,a,m,w);
				change(t[rt].rc,m+1,r,m+1,b,w);
			}
		}
		updata(rt);
	}
	int ask(int rt,int l,int r,int a,int b){
		if(l==a&&r==b){
			return t[rt].w;
		}
		pushdown(rt);
		int m=l+r>>1;
		if(b<=m){
			return ask(t[rt].lc,l,m,a,b);
		}
		if(a>m){
			return ask(t[rt].rc,m+1,r,a,b);
		}
		return ask(t[rt].lc,l,m,a,m)+ask(t[rt].rc,m+1,r,m+1,b)
		       -(t[t[rt].lc].r==t[t[rt].rc].l);
	}
	int ask(int rt,int l,int r,int &x){
		if(l==r){
			return t[rt].l;
		}
		pushdown(rt);
		int m=l+r>>1;
		if(x<=m){
			return ask(t[rt].lc,l,m,x);
		}
		else{
			return ask(t[rt].rc,m+1,r,x);
		}
	}
}t;
struct edge{
	int v,next;
}e[N];
int n,head[N],dfn[N],son[N],sum[N],dep[N],fa[N],rank[N],top[N],a[N]={-1},k,dfs_clock;
void add(int u,int v){
	e[++k]=(edge){v,head[u]};
	head[u]=k;
}
void dfs1(int u,int f,int d){
	dep[u]=d;
	sum[u]=1;
	fa[u]=f;
	son[u]=0;
	int i,v;
	for(i=head[u];i;i=e[i].next){
		v=e[i].v;
		if(v!=f){
			dfs1(v,u,d+1);
			sum[u]+=sum[v];
			if(sum[v]>sum[son[u]]){
				son[u]=v;
			}
		}
	}
}
void dfs2(int u,int t){
	top[u]=t;
	dfn[u]=++dfs_clock;
	rank[dfs_clock]=u;
	if(!son[u]){
		return;
	}
	dfs2(son[u],t);
	int i,v;
	for(i=head[u];i;i=e[i].next){
		v=e[i].v;
		if(v!=fa[u]&&v!=son[u]){
			dfs2(v,v);
		}
	}
}
void change(int x,int y,int &w){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]){
			swap(x,y);
		}
		t.change(1,1,n,dfn[top[x]],dfn[x],w);
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]){
		swap(x,y);
	}
	t.change(1,1,n,dfn[x],dfn[y],w);
}
int ask(int x,int y){
	int s=0,a,b;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]){
			swap(x,y);
		}
		s+=t.ask(1,1,n,dfn[top[x]],dfn[x])-(t.ask(1,1,n,dfn[top[x]])==t.ask(1,1,n,dfn[fa[top[x]]]));
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]){
		swap(x,y);
	}
	return s+t.ask(1,1,n,dfn[x],dfn[y]);
}
int main(){
	int m,u,v,i;
	char c[10];
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs1(1,0,1);
	dfs2(1,1);
	t.clear();
	t.build(1,1,n);
	for(i=1;i<=n;i++){
		t.change(1,1,n,dfn[i],dfn[i],a[i]);
	}
	while(m--){
		scanf("%s%d%d",c,&u,&v);
		if(c[0]=='Q'){
			printf("%d\n",ask(u,v));
		}
		else{
			scanf("%d",&i);
			change(u,v,i);
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值