【小结】树链剖分

树链剖分是把树上问题转化为区间问题的工具,额外耗时 \(O(log{n})\)

树链剖分不一定要套线段树。

P2486 [SDOI2011] 染色

直接区间染色,区间查询颜色段。难点在于线段树部分和链的拼接。

首先来看线段树。发现是好合并的,把两边颜色数相加,如果左边的右端点等于右边的左端点,那么减一。

然后修改就是把一段覆盖,标记是好下传的。

对于链的拼接:每一次记录要跳的那边的下面最后一个。然后判断是否相等。最后判一下即可。

#include<bits/stdc++.h>
using namespace std;
/*

*/
struct FSI{
	template<typename T>
	FSI& operator >> (T &res){
		res=0;T f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
		res*=f;
		return *this;
	}
}scan;
const int N=1e5+10;
int n,m,i,w[N];
int x,y,z,last[N],c;
int dep[N],son[N],sz[N];
int dfn[N],idx,id[N];
int top[N],v;
int f[N];
int lc,rc;
char op;
struct Edge{
	int to,next;
}e[N<<1];
struct Node{
	int l,r,sum,tag,L,R;
}a[N<<2];
void add(int u,int v)
{
	e[++c]={v,last[u]};
	last[u]=c;
}
void dfs(int x,int fa)
{
	int i,to;
	f[x]=fa;
	sz[x]=1;
	son[x]=0;
	dep[x]=dep[fa]+1;
	for (i=last[x];i;i=e[i].next)
	{
		to=e[i].to;
		if (to==fa) continue;
		dfs(to,x);
		sz[x]+=sz[to];
		if (sz[to]>sz[son[x]]) son[x]=to;
	}
}
void fun(int x)
{
	int i,to;
	dfn[x]=++idx;
	id[idx]=x;
	if (son[x])
	{
		top[son[x]]=top[x];
		fun(son[x]);
	}
	for (i=last[x];i;i=e[i].next)
	{
		to=e[i].to;
		if (to==f[x]||to==son[x]) continue;
		top[to]=to;
		fun(to);
	}
}
void pushup(int k)
{
	a[k].L=a[k<<1].L;
	a[k].R=a[k<<1|1].R;
	if (a[k<<1].R==a[k<<1|1].L) a[k].sum=a[k<<1].sum+a[k<<1|1].sum-1;
	else a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void build(int k,int l,int r)
{
	a[k].l=l;
	a[k].r=r;
	if (l==r)
	{
		a[k].sum=1;
		a[k].L=a[k].R=w[id[l]];
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void change(int k,int v)
{
	a[k].L=a[k].R=v;
	a[k].sum=1;
	a[k].tag=v;
}
void pushdown(int k)
{
	if (a[k].tag)
	{
		change(k<<1,a[k].tag);
		change(k<<1|1,a[k].tag);
		a[k].tag=0;
	}
}
void update(int k,int x,int y,int v)
{
	int l=a[k].l,r=a[k].r;
	if (l>=x&&r<=y)
	{
		change(k,v);
		return;
	}
	pushdown(k);
	int mid=l+r>>1;
	if (x<=mid) update(k<<1,x,y,v);
	if (y>mid) update(k<<1|1,x,y,v);
	pushup(k);
}
int query(int k,int x,int y)
{
	int l=a[k].l,r=a[k].r;
	if (l>=x&&r<=y) 
	{
		if (l==x) lc=a[k].L;
		if (r==y) rc=a[k].R;
		return a[k].sum;
	}
	pushdown(k);
	int mid=l+r>>1,res=0;
	if (x<=mid) res+=query(k<<1,x,y);
	if (y>mid) res+=query(k<<1|1,x,y);
	if (x<=mid&&y>mid&&a[k<<1].R==a[k<<1|1].L) res--;
	return res;
}
void update_path(int x,int y,int v)
{
	while (top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
		update(1,dfn[top[x]],dfn[x],v);
		x=f[top[x]];
	}
	if (dep[x]<dep[y]) swap(x,y);
	update(1,dfn[y],dfn[x],v);
}
int query_path(int x,int y)
{
	int res=0,cx=0,cy=0;
	while (top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y),swap(cx,cy);
		res+=query(1,dfn[top[x]],dfn[x]);
		if (rc==cx) res--;
		cx=lc;
		x=f[top[x]];
	}
	if (dep[x]<dep[y]) swap(x,y),swap(cx,cy);
	res+=query(1,dfn[y],dfn[x]);
	if (rc==cx) res--;
	if (lc==cy) res--;
	return res;
}
int main()
{
	scan>>n>>m;
	for (i=1;i<=n;i++) scan>>w[i];
	for (i=1;i<n;i++)
	{
		scan>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	top[1]=1;
	fun(1);
	build(1,1,n);
	while (m--)
	{
		scanf("%s",&op);
		scan>>x>>y;
		if (op=='C') scan>>z,update_path(x,y,z);
		else printf("%d\n",query_path(x,y));
	}
	return 0;
}

P7735 [NOI2021] 轻重边

和上一题有异曲同工之妙。

有一个难想的转化:每次做 1 操作相当于把路径上的点全部标为一个与其他点不相同的颜色,查询时查询相邻颜色相同点对。

然后就是上一题了。

注意线段树每个节点都要打 \(tag=-1\)

#include<bits/stdc++.h>
using namespace std;
/*
1
8 1
2 1
3 2
4 1
5 2
6 3
7 3
8 2
2 7 1
线段树初始 tag=-1 一定要赋值给全局,而不是 l=r。
*/
struct FSI{
	template<typename T>
	FSI& operator >> (T &res){
		res=0;T f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
		res*=f;
		return *this;
	}
}scan;
const int N=1e5+10;
int T,n,m,i,x,y,op,col;
int last[N],c;
int sz[N],son[N],f[N],dep[N];
int top[N],dfn[N],id[N],idx;
int lc,rc;
struct Edge{
	int to,next;
}e[N<<1];
struct Node{
	int l,r,sum,L,R,tag;
}a[N<<2];
void add(int u,int v)
{
	e[++c]={v,last[u]};
	last[u]=c;
}
void dfs(int x,int fa)
{
	int i,to;
	sz[x]=1;
	dep[x]=dep[fa]+1;
	f[x]=fa;
	for (i=last[x];i;i=e[i].next)
	{
		to=e[i].to;
		if (to==fa) continue;
		dfs(to,x);
		sz[x]+=sz[to];
		if (sz[to]>sz[son[x]]) son[x]=to;
	}
}
void fun(int x)
{
	int i,to;
	dfn[x]=++idx;
	id[idx]=x;
	if (son[x])
	{
		top[son[x]]=top[x];
		fun(son[x]);
	}
	for (i=last[x];i;i=e[i].next)
	{
		to=e[i].to;
		if (to==f[x]||to==son[x]) continue;
		top[to]=to;
		fun(to);
	}
}
void pushup(int k)
{
	a[k].L=a[k<<1].L;
	a[k].R=a[k<<1|1].R;
	if (a[k<<1].R==a[k<<1|1].L) a[k].sum=a[k<<1].sum+a[k<<1|1].sum+1;
	else a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void change(int k,int v)
{
	a[k].L=a[k].R=v;
	a[k].sum=a[k].r-a[k].l;
	a[k].tag=v;
}
void pushdown(int k)
{
	if (a[k].tag!=-1)
	{
		change(k<<1,a[k].tag);
		change(k<<1|1,a[k].tag);
		a[k].tag=-1;
	}
}
void build(int k,int l,int r)
{
	a[k].l=l;
	a[k].r=r;
	a[k].tag=-1;//线段树初始 tag=-1 一定要赋值给全局,而不是 l=r。
	if (l==r)
	{
		a[k].sum=0;
		a[k].L=a[k].R=++col;
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void update(int k,int x,int y,int v)
{
	int l=a[k].l,r=a[k].r;
	if (l>=x&&r<=y)
	{
		change(k,v);
		return;
	}
	pushdown(k);
	int mid=l+r>>1;
	if (x<=mid) update(k<<1,x,y,v);
	if (y>mid) update(k<<1|1,x,y,v);
	pushup(k);
}
int query(int k,int x,int y)
{
	int l=a[k].l,r=a[k].r;
	if (l>=x&&r<=y) 
	{
		if (l==x) lc=a[k].L;
		if (r==y) rc=a[k].R;
		return a[k].sum;
	}
	pushdown(k);
	int mid=l+r>>1,res=0;
	if (x<=mid) res+=query(k<<1,x,y);
	if (y>mid) res+=query(k<<1|1,x,y);
	if (x<=mid&&y>mid&&a[k<<1].R==a[k<<1|1].L) res++;
	return res;
}
void update_path(int x,int y,int v)
{
	while (top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
		update(1,dfn[top[x]],dfn[x],v);
		x=f[top[x]];
	}
	if (dep[x]<dep[y]) swap(x,y);
	update(1,dfn[y],dfn[x],v);
}
int query_path(int x,int y)
{
	int res=0,tx=-1,ty=-1,t;
	while (top[x]!=top[y])
	{
		if (dep[top[x]]<dep[top[y]]) swap(x,y),swap(tx,ty);
		t=query(1,dfn[top[x]],dfn[x]);
		if (tx==rc) t++;
		res+=t;
		tx=lc;
		x=f[top[x]];
	}
	if (dep[x]<dep[y]) swap(x,y),swap(tx,ty);
	t=query(1,dfn[y],dfn[x]);
	if (lc==ty) t++;
	if (rc==tx) t++;
	res+=t;
	return res;
}
void work()
{
	scan>>n>>m;
	for (i=1;i<=n;i++) last[i]=son[i]=0;
	col=0;
	c=0;
	idx=0;
	for (i=1;i<n;i++)
	{
		scan>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	top[1]=1;
	fun(1);
	build(1,1,n);
	while (m--)
	{
		scan>>op>>x>>y;
		if (op==1) update_path(x,y,++col);
		else printf("%d\n",query_path(x,y));
	}
}
int main()
{
	scan>>T;
	while (T--) work();
	return 0;
}
posted @ 2026-08-21 16:46  GUO120822  阅读(2)  评论(0)    收藏  举报