【题目】
题目描述:
给定一棵有 个节点的无根树和 个操作,操作有 类:
- 将节点 到节点 路径上所有点都染成颜色 ;
- 询问节点 到节点 路径上的颜色段数量(连续相同颜色被认为是同一段),如 由 段组成: 、 和 。
请你写一个程序依次完成这 个操作。
输入格式:
第一行包含 个整数 和 ,分别表示节点数和操作数;
第二行包含 个正整数表示 个节点的初始颜色;
下面 行每行包含两个整数 和 ,表示 和 之间有一条无向边。
下面 行每行描述一个操作:
表示这是一个染色操作,把节点 到节点 路径上所有点(包括 和 )都染成颜色 ;
表示这是一个询问操作,询问节点 到节点 (包括 和 )路径上的颜色段数量。
输出格式:
对于每个询问操作,输出一行答案。
样例数据:
输入
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
备注:
【数据范围】
对于测试点
、
、
、
,树是这样生成的:
随机生成一个 ~ 的排列 ,设 为根。对于 , 的父亲为 Prandom(1,i-1),其中 Prandom(a,b) 以相等的概率返回 中的一个元素,然后将所有边打乱顺序后作为输入提供给你的程序。
【友情提示】
不允许使用编译开关改变栈空间大小,请选手尽量不要使用递归,以避免堆栈溢出。
【分析】
先讲一下这道题如果不是在树上操作,而是在序列上(也就是链的情况)该怎么做
那么,操作 就是普通的区间修改,套线段树板子就行,难点是操作
对于每个线段树的节点,记录三个值: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;
}