#2134 染色 【树链剖分】

本文深入探讨了树链剖分算法,一种高效处理树形结构数据查询与更新的算法。通过实例讲解如何利用该算法解决特定类型的路径操作问题,包括颜色染色和颜色段查询。文章详细解释了算法的实现过程,包括数据结构的设计和关键函数的定义,为读者提供了全面的理解和实际应用的指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

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

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

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

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

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

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

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

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

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和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
数据范围:
n<=1e5,m<=1e5,int不会爆

思路:

基本上是一个裸的树链剖分 只是注意,维护查询路径的时候,

两个相邻的重链的端点值的颜色可能一样,–ans

所以要维护一个上一个的端点颜色

代码:
(全部用结构体更简洁。。。)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<vector>
#define lc (p<<1)
#define rc ((p<<1)|1)
#define pf printf
#define sf scanf
using namespace std;
const int maxn=1e5+10;
int n,m,seg[maxn],rev[maxn],top[maxn],dep[maxn],fa[maxn],son[maxn],siz[maxn],num[maxn];
int cl[maxn<<2],cr[maxn<<2],sum[maxn<<2],laz[maxn<<2]; 
vector <int> G[maxn];
struct pi{
	int s,cl,cr;
};
inline pi mergep(pi a,pi b){
	pi ans;
	ans.s=a.s+b.s;ans.cl=a.cl,ans.cr=b.cr;
	if(a.cr==b.cl)--ans.s;
	return ans;
	
}
void dfs1(int u,int f){
	dep[u]=dep[f]+1;fa[u]=f;siz[u]=1;
	for(int i=0;i<G[u].size();++i){
		int v=G[u][i];
		if(v==f)continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs2(int u){
	if(son[u]){
		seg[son[u]]=++seg[0];top[son[u]]=top[u];rev[seg[0]]=son[u];
		dfs2(son[u]);
	}
	for(int i=0;i<G[u].size();++i){
		int v=G[u][i];
		if(!top[v]){
			seg[v]=++seg[0];top[v]=v;rev[seg[0]]=v;
			dfs2(v);
		}
	}
}
inline void merge(int p){
	sum[p]=sum[lc]+sum[rc];
	cl[p]=cl[lc];cr[p]=cr[rc];
	if(cr[lc]==cl[rc])--sum[p];	
}
void build(int p,int l,int r){
	if(l==r){
		sum[p]=1;
		cl[p]=cr[p]=num[rev[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	merge(p);
}
inline void pass(int p){
	if(laz[p]){
		sum[lc]=sum[rc]=1;
		cl[lc]=cl[rc]=cr[lc]=cr[rc]=laz[p];
		laz[lc]=laz[rc]=laz[p];
	}
	laz[p]=0;
}
void change(int p,int l,int r,int ql,int qr,int k){
	if(l>=ql&&r<=qr){
		sum[p]=1;
		cl[p]=cr[p]=k;
		laz[p]=k;
		return;
	}
	pass(p);
	int mid=(l+r)>>1;
	if(qr<=mid) change(lc,l,mid,ql,qr,k);
	else if(ql>mid) change(rc,mid+1,r,ql,qr,k);
	else{
		change(lc,l,mid,ql,qr,k);change(rc,mid+1,r,ql,qr,k);
	}
	merge(p);
}
pi query(int p,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr){	
		return (pi){sum[p],cl[p],cr[p]};
	}
	pass(p);
	int mid=(l+r)>>1;
	if(qr<=mid)return query(lc,l,mid,ql,qr);
	else if(ql>mid)  return query(rc,mid+1,r,ql,qr);
	else{
		return mergep(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
	}
}
int answer(int x,int y){
	pi t;int ret=0;
	int fx=top[x],fy=top[y];
	int xcl=0,ycl=0;
	while(fx!=fy){
		if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy),swap(xcl,ycl);
		t=query(1,1,seg[0],seg[fx],seg[x]);
		if(t.cr==xcl)--t.s;
		ret+=t.s;xcl=t.cl;
		x=fa[fx];
		fx=top[x];
	}
	if(dep[x]>dep[y])swap(x,y),swap(xcl,ycl);
	t=query(1,1,seg[0],seg[x],seg[y]);
	if(xcl==t.cl)--t.s;
	if(ycl==t.cr)--t.s;
	return ret+t.s;
} 
void act(int x,int y,int k){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
		change(1,1,seg[0],seg[fx],seg[x],k);
		x=fa[fx];//跳过一条轻边 
		fx=top[x];
	}
	if(dep[x]>dep[y])swap(x,y);
	change(1,1,seg[0],seg[x],seg[y],k);
}
char s;
signed main (){
	sf("%d%d",&n,&m);for(int i=1;i<=n;++i)sf("%d",&num[i]);
	for(int i=2;i<=n;++i){
		int a,b;sf("%d%d",&a,&b);
		G[a].push_back(b);G[b].push_back(a);
	}
	top[1]=seg[1]=rev[1]=seg[0]=1;
	dfs1(1,0);dfs2(1);build(1,1,seg[0]);
	while(m--){
		int a,b,c;sf("\n%c%d%d",&s,&a,&b);
		if(s=='Q')printf("%d\n",answer(a,b));	
		else{
			sf("%d",&c);act(a,b,c);
		}
	}
	   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值