BZOJ2243[SDOI2011] 染色

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2243

染色

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<=105,操作数M<=105,所有的颜色C为整数且在[0, 10^9]之间。

题解

对于每个点,维护最左端的颜色和最右端的颜色以及自己这一段颜色的总数就好了。

代码
#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
using namespace std;
const int M=1e5+5;
int dad[M],son[M][2],lcol[M],rcol[M],col[M],tot[M],dye[M],n,m;
bool rev[M];
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v)
{
	lcol[v]=ls?lcol[ls]:col[v];rcol[v]=rs?rcol[rs]:col[v];
	if(ls&&rs)tot[v]=tot[ls]+tot[rs]+1-(rcol[ls]==col[v])-(lcol[rs]==col[v]);
	else if(ls)tot[v]=tot[ls]+(rcol[ls]!=col[v]);
	else if(rs)tot[v]=tot[rs]+(lcol[rs]!=col[v]);
	else tot[v]=1;
}
void turn(int v){swap(ls,rs);swap(lcol[v],rcol[v]);rev[v]^=1;}
void pdye(int v,int c){dye[v]=col[v]=lcol[v]=rcol[v]=c;tot[v]=1;}
void down(int v)
{
	if(dye[v]){if(ls)pdye(ls,dye[v]);if(rs)pdye(rs,dye[v]);dye[v]=0;}
	if(rev[v]){if(ls)turn(ls);if(rs)turn(rs);rev[v]^=1;}
}
void push(int v){if(notroot(v))push(dad[v]);down(v);}
void spin(int v)
{
	int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
	if(notroot(f))son[ff][son[ff][1]==f]=v;
	son[v][!k]=f;son[f][k]=w;
	if(w)dad[w]=f;
	dad[f]=v;dad[v]=ff;
	up(f);up(v);
}
void splay(int v)
{
	push(v);
	int f,ff;
	while(notroot(v))
	{
		f=dad[v];ff=dad[f];
		if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
		spin(v);
	}
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v);splay(v);turn(v);}
void split(int x,int y){beroot(x);access(y);splay(y);}
void link(int x,int y){beroot(x);dad[x]=y;}
void in()
{
	int a,b;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&col[i]);
		lcol[i]=rcol[i]=col[i];
		tot[i]=1;
	}
	for(int i=1;i<n;++i)
	scanf("%d%d",&a,&b),link(a,b);
}
void ac()
{
	char ch[10];
	int a,b,c;
	for(int i=1;i<=m;++i)
	{
		scanf("%s%d%d",ch,&a,&b);
		if(ch[0]=='C')scanf("%d",&c),split(a,b),pdye(b,c);
		else split(a,b),printf("%d\n",tot[b]);
	}
}
int main()
{
	in();ac();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值