【SDOI 2011】染色

【题目】

传送门

题目描述:

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

  1. 将节点 a a a 到节点 b b b 路径上所有点都染成颜色 c c c
  2. 询问节点 a a a 到节点 b b b 路径上的颜色段数量(连续相同颜色被认为是同一段),如 “ 112221 ” “112221” 112221 3 3 3 段组成: “ 11 ” “11” 11 “ 222 ” “222” 222 “ 1 ” “1” 1

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

输入格式:

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

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

下面 n − 1 n-1 n1 行每行包含两个整数 x x x y y y ,表示 x x x y y y 之间有一条无向边。

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

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

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

输出格式:

对于每个询问操作,输出一行答案。

样例数据:

输入
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

输出
3
1
2

备注:

【数据范围】
在这里插入图片描述
对于测试点 7 7 7 8 8 8 9 9 9 10 10 10,树是这样生成的:

随机生成一个 1 1 1 n n n 的排列 p p p ,设 p 1 p_1 p1 为根。对于 2 ≤ i ≤ n 2≤i≤n 2in p i p_i pi 的父亲为 Prandom(1,i-1),其中 Prandom(a,b) 以相等的概率返回 { x ∈ Z ∣ a ≤ x ≤ b } \{x∈Z|a≤x≤b\} {xZaxb} 中的一个元素,然后将所有边打乱顺序后作为输入提供给你的程序。

【友情提示】

不允许使用编译开关改变栈空间大小,请选手尽量不要使用递归,以避免堆栈溢出。


【分析】

先讲一下这道题如果不是在树上操作,而是在序列上(也就是链的情况)该怎么做

那么,操作 1 1 1 就是普通的区间修改,套线段树板子就行,难点是操作 2 2 2

对于每个线段树的节点,记录三个值:L 是序列最左边的颜色,R 是序列最右边的颜色,num 是这个区间的颜色段数量

假设 x 是当前节点,lc(x)x 的左区间,rc(x)x 的右区间

那么显然,若 R[lc(x)]=L[rc(x)],则 num[x]=num[lc(x)]+num[rc(x)]-1;否则 num[x]=num[lc(x)]+num[rc(x)]

知道了这个之后,在序列上的问题就迎刃而解了

现在把问题转移到树上来,其实也是差不多的思想,可以先用树剖把树划成链,对链建线段树,注意一点细节就可以了


【代码】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100005
using namespace std;
int n,m,t,tot;
int first[N],v[N<<1],nxt[N<<1];
int a[N],fa[N],dep[N],idx[N],son[N],top[N],pos[N],Size[N];
struct Tree
{
	int L,R,num,mark;
	#define L(x) Seg[x].L
	#define R(x) Seg[x].R
	#define num(x) Seg[x].num
	#define mark(x) Seg[x].mark
}Seg[N<<2];
void add(int x,int y)
{
	t++;
	nxt[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void dfs1(int x)
{
	int i,k;
	Size[x]=1,son[x]=0;
	for(i=first[x];i;i=nxt[i])
	{
		k=v[i];
		if(k==fa[x])  continue;
		fa[k]=x,dep[k]=dep[x]+1;
		dfs1(k),Size[x]+=Size[k];
		if(Size[k]>Size[son[x]])  son[x]=k;
	}
}
void dfs2(int x,int tp)
{
	int i,k;
	top[x]=tp,pos[x]=++tot,idx[tot]=x;
	if(son[x])  dfs2(son[x],tp);
	for(i=first[x];i;i=nxt[i])
	{
		k=v[i];
		if(k!=son[x]&&k!=fa[x])
		  dfs2(k,k);
	}
}
Tree pushup(Tree left,Tree right)
{
	Tree now;
	now.mark=0;
	now.L=left.L,now.R=right.R;
	now.num=left.num+right.num;
	if(left.R==right.L)  now.num--;
	return now;
}
void build(int root,int l,int r)
{
	if(l==r)
	{
		num(root)=1;
		L(root)=R(root)=a[idx[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	Seg[root]=pushup(Seg[root<<1],Seg[root<<1|1]);
}
void pushdown(int root)
{
	if(!mark(root))  return;
	L(root<<1)=R(root<<1)=mark(root<<1)=mark(root);
	L(root<<1|1)=R(root<<1|1)=mark(root<<1|1)=mark(root);
	num(root<<1)=num(root<<1|1)=1,mark(root)=0;
}
Tree query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return Seg[root];
	int mid=(l+r)>>1;pushdown(root);
	if(y<=mid)  return query(root<<1,l,mid,x,y);
	if(x>mid)  return query(root<<1|1,mid+1,r,x,y);
	return pushup(query(root<<1,l,mid,x,y),query(root<<1|1,mid+1,r,x,y));
}
int ask(int x,int y)
{
	int ans=0,xx=0,yy=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y),swap(xx,yy);
		Tree res=query(1,1,n,pos[top[x]],pos[x]);ans+=res.num;
		if(xx==res.R)  ans--;  xx=res.L;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y),swap(xx,yy);
	Tree res=query(1,1,n,pos[x],pos[y]);ans+=res.num;
	if(xx==res.L)ans--;  if(yy==res.R)ans--;
	return ans;
}
void modify(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	{
		num(root)=1;
		L(root)=R(root)=mark(root)=k;
		return;
	}
	int mid=(l+r)>>1;pushdown(root);
	if(x<=mid)  modify(root<<1,l,mid,x,y,k);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,k);
	Seg[root]=pushup(Seg[root<<1],Seg[root<<1|1]);
}
void change(int x,int y,int k)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])  swap(x,y);
		modify(1,1,n,pos[top[x]],pos[x],k);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])  swap(x,y);
	modify(1,1,n,pos[x],pos[y],k);
}
int main()
{
	int x,y,i,k;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	for(i=1;i<n;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	dfs1(1),dfs2(1,1),build(1,1,n);
	for(i=1;i<=m;++i)
	{
		char c;  cin>>c;
		scanf("%d%d",&x,&y);
		if(c=='Q')  printf("%d\n",ask(x,y));
		else  scanf("%d",&k),change(x,y,k);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值