题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=2243
题解:
树剖的题,差不多算是裸题吧,线段树中维护三个值,为
tr [ id ],表示 id 所代表的区间中总共有多少段颜色。
lx [ id ],表示 id 所代表的区间中最左端的点的颜色。
rx [ id ],表示 id 所代表的区间中最右端的点的颜色。
然后按树剖的做法往下做即可,注意在树剖中查询后合并两段查询结果时的细节,需要判断一下合并的两个接点颜色是否相同,具体看代码。
代码:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<vector>
#define maxn (100005)
#define lson (id*2)
#define rson (id*2+1)
using namespace std;
int tr[4*maxn],lx[4*maxn],rx[4*maxn],lazy[4*maxn];
//tr,lx,rx,lazy,线段树中维护的操作
int tot,n,m,fu[maxn],c[maxn],vis[maxn],in[maxn],size[maxn],son[maxn],fa[maxn],dp[maxn],top[maxn];
//这些都是树剖时用的信息
vector<int>lin[maxn];
char p[3];
//dfs1找重链
void dfs1(int x,int f,int d)
{
size[x]=1;
fa[x]=f;
dp[x]=d;
for (int i=0;i<lin[x].size();i++)
if (lin[x][i]!=f)
{
dfs1(lin[x][i],x,d+1);
size[x]+=size[lin[x][i]];
if (son[x]==0||size[son[x]]<size[lin[x][i]])
son[x]=lin[x][i];
}
}
//dfs2将节点对应在线段树中
void dfs2(int x,int f)
{
tot++;
in[x]=tot;
fu[tot]=x;
top[x]=f;
if (son[x])
dfs2(son[x],f);
for (int i=0;i<lin[x].size();i++)
{
int v=lin[x][i];
if (v!=son[x]&&v!=fa[x])
dfs2(v,v);
}
}
//注意pushup和pushdown的方法
void pushup(int id)
{
tr[id]=tr[lson]+tr[rson];
if (rx[lson]==lx[rson])
tr[id]-=1;
rx[id]=rx[rson];
lx[id]=lx[lson];
}
void pushdown(int id)
{
if (lazy[id]!=-1)
{
lazy[lson]=lazy[id];
lazy[rson]=lazy[id];
lx[lson]=rx[lson]=lazy[id];
lx[rson]=rx[rson]=lazy[id];
lazy[id]=-1;
}
}
void build(int id,int l,int r)
{
lazy[id]=-1;
if (l>r) return ;
if (l==r)
{tr[id]=1;lx[id]=c[fu[l]];rx[id]=c[fu[l]];return ;}
int mid=(l+r)/2;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(id);
}
int ans;
//查询区间(L,R)内有多少颜色段
void query_s(int id,int l,int r,int L,int R)
{
if (l>r) return ;
if (l>=L&&r<=R) {ans+=tr[id];return ;}
int mid=(l+r)/2;
pushdown(id);
if (R<=mid)
query_s(lson,l,mid,L,R);
else if (L>=mid+1)
query_s(rson,mid+1,r,L,R);
else
{
if (rx[lson]==lx[rson])
ans-=1;
query_s(lson,l,mid,L,mid);
query_s(rson,mid+1,r,mid+1,R);
}
}
//查询点x是什么颜色
int query_c(int id,int l,int r,int x)
{
if (l==r&&x==r) return lx[id];
int mid=(l+r)/2;
pushdown(id);
if (x<=mid)
return query_c(lson,l,mid,x);
else
return query_c(rson,mid+1,r,x);
}
//将区间(L,R)内的颜色改为v
void change(int id,int l,int r,int L,int R,int v)
{
if (l>r) return ;
if (l>=L&&r<=R)
{
tr[id]=1;lx[id]=v;rx[id]=v;lazy[id]=v;
return ;
}
int mid=(l+r)/2;
pushdown(id);
if (L<=mid) change(lson,l,mid,L,R,v);
if (R>=mid+1) change(rson,mid+1,r,L,R,v);
pushup(id);
}
//注意以上查询的L,R,x都是线段树位置中的L,R,x,而不是树上的节点
//从这里以后的l,r表示树上的节点l,r
int query_tree(int l,int r)
{
int f1=top[l];
int f2=top[r];
ans=0;
while(f1!=f2)
{
if (dp[f1]<dp[f2])
{
swap(f1,f2);
swap(l,r);
}
query_s(1,1,n,in[f1],in[l]);
if (query_c(1,1,n,in[f1])==query_c(1,1,n,in[fa[f1]]))
ans-=1;
l=fa[f1];
f1=top[fa[f1]];
}
if (dp[l]<dp[r])
swap(l,r);
query_s(1,1,n,in[r],in[l]);
return ans;
}
void add_tree(int l,int r,int v)
{
int f1=top[l];
int f2=top[r];
while(f1!=f2)
{
if (dp[f1]<dp[f2])
{
swap(f1,f2);
swap(l,r);
}
change(1,1,n,in[f1],in[l],v);
l=fa[f1];
f1=top[fa[f1]];
}
if (dp[l]<dp[r])
swap(l,r);
change(1,1,n,in[r],in[l],v);
return ;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&c[i]);
for (int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
lin[x].push_back(y);
lin[y].push_back(x);
}
dfs1(1,1,1);
dfs2(1,1);
build(1,1,n);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%s",p);
if (p[0]=='C')
{
scanf("%d%d%d",&x,&y,&z);
add_tree(x,y,z);
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",query_tree(x,y));
}
}
}