题目链接
给定一棵 n n 个节点的无根树,共有 m m 个操作,操作分为两种:
将节点 a a 到节点 b b 的路径上的所有点 (包括 a a 和 b b ) 都染成颜色 c c 。
询问节点 a a 到节点 b b 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:11
、 222
、 1
。
输入格式
输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 n n 和操作个数 m m 。
第二行有 n n 个用空格隔开的整数,第 i i 个整数 w i w i 代表结点 i i 的初始颜色。
第 3 3 到第 ( n + 1 ) ( n + 1 ) 行,每行两个用空格隔开的整数 u , v u , v ,代表树上存在一条连结节点 u u 和节点 v v 的边。
第 ( n + 2 ) ( n + 2 ) 到第 ( n + m + 1 ) ( n + m + 1 ) 行,每行描述一个操作,其格式为:
每行首先有一个字符 o p o p ,代表本次操作的类型。
若 o p o p 为 C
,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 a , b , c a , b , c ,代表将 a a 到 b b 的路径上所有点都染成颜色 c 。 c 。
若 o p o p 为 Q
,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 a , b a , b ,表示查询 a a 到 b b 路径上的颜色段数量。
输出格式
对于每次查询操作,输出一行一个整数代表答案。
输入输出样例
输入
输出
说明/提示
数据规模与约定
对于 100 % 100 % 的数据, 1 ≤ n , m ≤ 10 5 , 1 ≤ w i , c ≤ 10 9 , 1 ≤ a , b , u , v ≤ n 1 ≤ n , m ≤ 10 5 , 1 ≤ w i , c ≤ 10 9 , 1 ≤ a , b , u , v ≤ n ,o p o p 一定为 C C 或 Q Q , 保证给出的图是一棵树。
解题思路
树链剖分
利用树链剖分思想将问题转换为一段连续区间上的操作,即统计区间上颜色段的数量,对于一段区间来说,先分为左右部分,统计左右部分颜色段的数量之和,如果左部分和右部分中间的颜色一样则说明该颜色段重复计算了,减一即可,所以线段树中应该存储的信息有 区间颜色段
,区间左边颜色
,区间右边颜色
,但由于自上而下更新时如果当前区间完全在修改区间内,这时会直接返回,下面范围更小的区间还没有更新,所以需要打懒标记。另外树链剖分形成的区间可能是多段,段与段之间也可能出现相同颜色,这时也会重复,查询时记录两边相邻的颜色,最后处理哪一段判断相应的颜色是否相等即可
时间复杂度:O ( ( n + m ) × l o g n ) O ( ( n + m ) × l o g n )
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int , int > PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax (T &x, T y) { return (y > x) ? x = y, 1 : 0 ; }
template <typename T> bool chkMin (T &x, T y) { return (y < x) ? x = y, 1 : 0 ; }
template <typename T> void inline read (T &x) {
int f = 1 ; x = 0 ; char s = getchar ();
while (s < '0' || s > '9' ) { if (s == '-' ) f = -1 ; s = getchar (); }
while (s <= '9' && s >= '0' ) x = x * 10 + (s ^ 48 ), s = getchar ();
x *= f;
}
const int N=1e5 +5 ;
int n,m,c[N],nc[N],id[N],cnt,sz[N],fa[N],dep[N],son[N],top[N];
vector<int > adj[N];
struct T
{
int l,r;
int lc,rc,sum,col;
}tr[N<<2 ];
void pushup (int u)
{
tr[u].sum=tr[u<<1 ].sum+tr[u<<1 |1 ].sum;
if (tr[u<<1 ].rc==tr[u<<1 |1 ].lc)tr[u].sum--;
tr[u].lc=tr[u<<1 ].lc;
tr[u].rc=tr[u<<1 |1 ].rc;
}
void pushdown (int u)
{
if (tr[u].col)
{
tr[u<<1 ].lc=tr[u<<1 ].rc=tr[u<<1 |1 ].lc=tr[u<<1 |1 ].rc=tr[u].col;
tr[u<<1 ].col=tr[u<<1 |1 ].col=tr[u].col;
tr[u<<1 ].sum=tr[u<<1 |1 ].sum=1 ;
tr[u].col=0 ;
}
}
void build (int u,int l,int r)
{
tr[u]={l,r};
if (l==r)
{
tr[u].lc=tr[u].rc=nc[l];
tr[u].sum=1 ;
return ;
}
int mid=l+r>>1 ;
build (u<<1 ,l,mid);
build (u<<1 |1 ,mid+1 ,r);
pushup (u);
}
void update (int u,int l,int r,int d)
{
if (l<=tr[u].l&&tr[u].r<=r)
{
tr[u].lc=tr[u].rc=tr[u].col=d;
tr[u].sum=1 ;
return ;
}
pushdown (u);
int mid=tr[u].l+tr[u].r>>1 ;
if (l<=mid)update (u<<1 ,l,r,d);
if (r>mid)update (u<<1 |1 ,l,r,d);
pushup (u);
}
T ask (int u,int l,int r)
{
if (l<=tr[u].l&&tr[u].r<=r)return tr[u];
int mid=tr[u].l+tr[u].r>>1 ;
pushdown (u);
if (l<=mid&&r>mid)
{
T res;
T L=ask (u<<1 ,l,r),R=ask (u<<1 |1 ,l,r);
res.sum=L.sum+R.sum;
res.lc=L.lc,res.rc=R.rc;
if (L.rc==R.lc)
res.sum--;
return res;
}
if (l<=mid)return ask (u<<1 ,l,r);
return ask (u<<1 |1 ,l,r);
}
void update_path (int x,int y,int d)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]])swap (x,y);
update (1 ,id[top[x]],id[x],d);
x=fa[top[x]];
}
if (dep[x]<dep[y])swap (x,y);
update (1 ,id[y],id[x],d);
}
int ask_path (int x,int y)
{
int res=0 ,lx=0 ,ly=0 ;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]])swap (x,y),swap (lx,ly);
T t=ask (1 ,id[top[x]],id[x]);
res+=t.sum;
if (t.rc==lx)res--;
lx=t.lc;
x=fa[top[x]];
}
if (dep[x]>dep[y])swap (x,y),swap (lx,ly);
T t=ask (1 ,id[x],id[y]);
res+=t.sum;
if (t.lc==lx)res--;
if (t.rc==ly)res--;
return res;
}
void dfs1 (int x,int father,int depth)
{
fa[x]=father,sz[x]=1 ,dep[x]=depth;
for (int y:adj[x])
{
if (y==father)continue ;
dfs1 (y,x,depth+1 );
sz[x]+=sz[y];
if (sz[son[x]]<sz[y])son[x]=y;
}
}
void dfs2 (int x,int t)
{
id[x]=++cnt,nc[cnt]=c[x],top[x]=t;
if (!son[x])return ;
dfs2 (son[x],t);
for (int y:adj[x])
{
if (y==fa[x]||y==son[x])continue ;
dfs2 (y,y);
}
}
int main ()
{
scanf ("%d%d" ,&n,&m);
for (int i=1 ;i<=n;i++)scanf ("%d" ,&c[i]);
for (int i=1 ;i<n;i++)
{
int x,y;
scanf ("%d%d" ,&x,&y);
adj[x].pb (y),adj[y].pb (x);
}
dfs1 (1 ,1 ,1 );
dfs2 (1 ,1 );
build (1 ,1 ,n);
while (m--)
{
char op[2 ];
int a,b,c;
scanf ("%s%d%d" ,op,&a,&b);
if (*op=='C' )
{
scanf ("%d" ,&c);
update_path (a,b,c);
}
else
printf ("%d\n" ,ask_path (a,b));
}
return 0 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!