这道题,显然我们需要树剖一下
然后我们考虑怎么用线段树去维护区间颜色段数
我们会想到我们需要维护这么几个东西
1.区间段数
2.区间左端点的颜色
3.区间右端点的颜色
合并的时候就是左儿子的段数加上右儿子的段数,如果左儿子的右端点和右儿子的左端点是一个颜色,那么就段数-1
因为是区间修改,所以加上一个懒标记就可以了
更新跟普通树剖一样,但是查询不太一样
这里提供一种适用范围更广的做法
我是受到了这道题的启发,那道题是求树上最大子段和,需要维护的东西比较多,所以单纯用变量不太好做。
所以我让我们的 函数返回的是一个结构体来储存这一段的答案的段数,左端点,右端点分别是什么,然后我们再写一个 函数来合并两个结构体,其实写起来和 差不多,大概是这个样子的
segment_tree merge(segment_tree l,segment_tree r){
if(!l.sum)return r;
if(!r.sum)return l;
segment_tree res;
res.lcol=l.lcol,res.rcol=r.rcol;
res.sum=l.sum+r.sum;
if(l.rcol==r.lcol)res.sum--;
return res;
}
那么我们树剖查询的时候,开两个 类型的结构体变量l,r,分别表示x往上跳到他们的lca的这一段的颜色段数和y跳到 这一段的颜色段数
然后依次合并就可以了
但是注意一点, 函数是不满足交换律的,所以我们要把 这一段放在左边。
最后合并的时候要把l的左端点颜色和右端点颜色交换一下,因为我们要把一个区间整体反转才能接到一起(可以画个图理解一下)
然后如果你只过了hack数据的话就查一下你的查询那一段有没有查反就好了
:
# include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=1e5+5;
template <typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m;
int head[N],cnt;
int a[N],_a[N];
int faz[N],son[N],dep[N],siz[N],top[N],dfn[N],tot;
struct Edge{
int to,next;
}e[N<<1];
void add(int x,int y){
e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}
struct segment_tree{
int l,r;
int lcol,rcol,sum;
int tag;
segment_tree(){l=r=lcol=rcol=sum=tag=0;}
}seg[N<<2];
# define lc (u<<1)
# define rc (u<<1|1)
void pushup(int u){
seg[u].sum=seg[lc].sum+seg[rc].sum;
if(seg[lc].rcol==seg[rc].lcol)seg[u].sum--;
seg[u].lcol=seg[lc].lcol;
seg[u].rcol=seg[rc].rcol;
}
void pushdown(int u){
seg[lc].sum=1;
seg[lc].lcol=seg[lc].rcol=seg[u].tag;
seg[lc].tag=seg[u].tag;
seg[rc].sum=1;
seg[rc].lcol=seg[rc].rcol=seg[u].tag;
seg[rc].tag=seg[u].tag;
seg[u].tag=0;
}
segment_tree merge(segment_tree l,segment_tree r){
if(!l.sum)return r;
if(!r.sum)return l;
segment_tree res;
res.lcol=l.lcol,res.rcol=r.rcol;
res.sum=l.sum+r.sum;
if(l.rcol==r.lcol)res.sum--;
return res;
}
void build(int u,int l,int r){
seg[u].l=l,seg[u].r=r;
if(l==r){seg[u].sum=1,seg[u].lcol=seg[u].rcol=_a[l];return;}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int k){
if(seg[u].l>=l&&seg[u].r<=r){
seg[u].sum=1;
seg[u].lcol=seg[u].rcol=k;
seg[u].tag=k;
return;
}
if(seg[u].tag)pushdown(u);
int mid=seg[u].l+seg[u].r>>1;
if(l<=mid)update(lc,l,r,k);
if(r>mid)update(rc,l,r,k);
pushup(u);
}
segment_tree query(int u,int l,int r){
if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
if(seg[u].tag)pushdown(u);
int mid=seg[u].l+seg[u].r>>1;
if(r<=mid)return query(lc,l,r);
if(l>mid)return query(rc,l,r);
return merge(query(lc,l,r),query(rc,l,r));
}
void RouteModify(int x,int y,int k){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,dfn[top[x]],dfn[x],k);
x=faz[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,dfn[x],dfn[y],k);
}
int RouteQuery(int x,int y){
segment_tree l,r;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
r=merge(query(1,dfn[top[y]],dfn[y]),r);
y=faz[top[y]];
}
else{
l=merge(query(1,dfn[top[x]],dfn[x]),l);
x=faz[top[x]];
}
}
if(dep[x]<dep[y])r=merge(query(1,dfn[x],dfn[y]),r);
else l=merge(query(1,dfn[y],dfn[x]),l);
swap(l.lcol,l.rcol);
return merge(l,r).sum;
}
void dfs1(int u,int fa){
faz[u]=fa;
siz[u]=1;
dep[u]=dep[fa]+1;
RepG(i,u){
int v=e[i].to;
if(v==fa)continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int _top){
top[u]=_top;
dfn[u]=++tot;
_a[tot]=a[u];
if(!son[u])return;
dfs2(son[u],_top);
RepG(i,u){
int v=e[i].to;
if(v==faz[u]||v==son[u])continue;
dfs2(v,v);
}
}
int main()
{
memset(head,-1,sizeof(head));
read(n),read(m);
Rep(i,1,n)read(a[i]);
Rep(i,1,n-1){
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs1(1,0),dfs2(1,1);
build(1,1,n);
Rep(i,1,m){
char opt[10];
int x,y,z;
scanf("%s",opt);
if(opt[0]=='C'){
read(x),read(y),read(z);
RouteModify(x,y,z);
}
else{
read(x),read(y);
printf("%d\n",RouteQuery(x,y));
}
}
return 0;
}