Bzoj-2243 [SDOI2011]染色(动态树/树链剖分)

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“1122213段组成:“11、“222和“1

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数nm,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数xy,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括ab)都染成颜色c

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括ab)路径上的颜色段数量。

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]之间。

Source

第一轮day1


PS:通过这道题我发现之前的LCT模板有点问题。。



#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 100015
using namespace std;
int n,m,u,v,c,lc[MAXN],rc[MAXN],ch[MAXN][2],s[MAXN],a[MAXN],value[MAXN],fa[MAXN],lazy2[MAXN];
bool lazy[MAXN];
inline bool isroot(int x)
{
	return ch[fa[x]][0] != x && ch[fa[x]][1] != x;
}
void push_up(int x)
{
	value[x] = 1;
	if(ch[x][0])
	{
		if(rc[ch[x][0]] == a[x]) value[x] = value[ch[x][0]];
		else value[x] += value[ch[x][0]];
		lc[x] = lc[ch[x][0]];
	}
	else lc[x] = a[x];
	if(ch[x][1])
	{
		if(lc[ch[x][1]] == a[x]) value[x] += value[ch[x][1]] - 1;
		else value[x] += value[ch[x][1]];
		rc[x] = rc[ch[x][1]];
	}
	else rc[x] = a[x];
}
void rotate(int x)
{
	int y = fa[x],z = fa[y];
	int d = ch[y][0] == x ? 0 : 1;
	if(!isroot(y))
	{
		if(ch[z][0] == y) ch[z][0] = x;
		else ch[z][1] = x;
	}
	fa[y] = x,fa[x] = z,fa[ch[x][d^1]] = y;
	ch[y][d] = ch[x][d^1],ch[x][d^1] = y;
	push_up(y),push_up(x);
}
void change(int x,int y)
{
	lazy2[x] = y;
	a[x] = lc[x] = rc[x] = y;
	value[x] = 1;
}
inline void push_down(int x)
{
	if(lazy[x])
	{
		int ls = ch[x][0],rs = ch[x][1];
		lazy[x]^=1,lazy[ls]^=1;lazy[rs]^=1;
		swap(ch[ls][0],ch[ls][1]);
		swap(ch[rs][0],ch[rs][1]);
		swap(lc[ls],rc[ls]);
		swap(lc[rs],rc[rs]);
	}
	if(lazy2[x] >= 0)
	{
		change(ch[x][0],lazy2[x]),change(ch[x][1],lazy2[x]);
		lazy2[x] = -1;
	}
}
void splay(int x)
{
	int tot = 0;s[++tot] = x;
	for(int i = x;!isroot(i);i = fa[i]) s[++tot] = fa[i];
	for(;tot;tot--) push_down(s[tot]);
	while(!isroot(x))
	{
		int y = fa[x],z = fa[y];
		if(!isroot(y))
		{
			if((ch[z][0] == y) ^ (ch[y][0] == x)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}
void access(int x)
{
	int t = 0;
	while(x)
	{
		splay(x);
		ch[x][1] = t;          //parent path 边中儿子一定是子链root,父亲是子链root实际父亲 
		push_up(x);
		t = x,x = fa[x];
	}
}
void makeroot(int x)        //x变成根 
{
	access(x),splay(x);
	swap(ch[x][0],ch[x][1]);
	lazy[x]^=1; 
}
void link(int x,int y)
{
	makeroot(x);fa[x] = y;
}
int main()
{
	memset(lazy2,-1,sizeof(lazy2));
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++) 
	{
		scanf("%d",&a[i]);
		value[i] = 1;
		lc[i] = rc[i] = a[i];
	}
	for(int i = 1;i < n;i++)
	{
		scanf("%d%d",&u,&v);
		link(u,v);
	}
	for(int i = 1;i <= m;i++)
	{
		char op[2];
		scanf("%s",op);
		if(op[0] == 'C') 
		{
			scanf("%d%d%d",&u,&v,&c);
			makeroot(u);
			access(v);
			splay(v);
			change(v,c);
		}
		else 
		{
			scanf("%d%d",&u,&v);
			makeroot(v);
			access(u);
			splay(u);
			printf("%d\n",value[u]);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值