树链剖分

树链剖分,亦称轻重边剖分。

把树的任意一条链转换成若干条重链,以解决一些树上问题。

这篇文章讲的很好,在这里就不在赘述了。

例1:Luogu P3384 【模板】轻重链剖分/树链剖分

一道树链剖分的模板题,用线段树维护,比较经典,复杂度 \(O(n\log^2 n)\) 的。比较长,有 3.4k。

#include <bits/stdc++.h>
#define Rint register int
using namespace std;
const int maxn=1e5+5;
int n,m,r,p,w[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0){x=-x;putchar('-');}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}

//fread & fwrite

struct node{int to,nxt;}e[maxn<<1];
int head[maxn],cnt;
inline void add_edge(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

//addedge

int dep[maxn],fa[maxn],size[maxn],son[maxn];
inline void dfs1(int x,int f,int deep)
{
	dep[x]=deep;fa[x]=f;size[x]=1;
	int hson=-1;
	for(Rint i=head[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==f)continue;
		dfs1(v,x,deep+1);
		size[x]+=size[v];
		if(size[v]>hson)hson=size[v],son[x]=v;
	}
}
int wt[maxn],id[maxn],top[maxn],cnt1=0;
inline void dfs2(int x,int topf)
{
	id[x]=++cnt1;wt[cnt1]=w[x];top[x]=topf;
	if(!son[x])return;
	dfs2(son[x],topf);
	for(Rint i=head[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[x]||v==son[x])continue;
		dfs2(v,v);
	}
}

//dfs

#define mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
int laz[maxn<<2],a[maxn<<2],res;
inline void pushdown(int rt,int lenn)
{
	laz[ls]+=laz[rt];laz[rs]+=laz[rt];
	a[ls]+=laz[rt]*(lenn-(lenn>>1));
	a[rs]+=laz[rt]*(lenn>>1);
	a[ls]%=p;a[rs]%=p;laz[rt]=0;
}
inline void build(int rt,int l,int r)
{
	if(l==r)
	{
		a[rt]=wt[l];
		if(a[rt]>p)a[rt]%=p;
		return;
	}
	build(ls,l,mid);build(rs,mid+1,r);
	a[rt]=(a[ls]+a[rs])%p;
}
inline void query(int rt,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)
	{
		res+=a[rt];
		if(res>=p)res%=p;
		return;
	}
	if(laz[rt])pushdown(rt,r-l+1);
	if(L<=mid)query(ls,l,mid,L,R);
	if(R>mid)query(rs,mid+1,r,L,R);
}
inline void update(int rt,int l,int r,int L,int R,int k)
{
	if(L<=l&&r<=R)
	{
		laz[rt]+=k;
		a[rt]+=k*(r-l+1); 
		return;
	}
	if(laz[rt])pushdown(rt,r-l+1);
	if(L<=mid)update(ls,l,mid,L,R,k);
	if(R>mid)update(rs,mid+1,r,L,R,k);
	a[rt]=(a[ls]+a[rs])%p;
}

//Segment Tree

inline int qR(int x,int y)
{
	int ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=0;
		query(1,1,n,id[top[x]],id[x]);
		ans+=res;ans%=p;x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	res=0;
	query(1,1,n,id[x],id[y]);
	ans+=res;
	return ans%p;
}
inline void updR(int x,int y,int z)
{
	z%=p;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		update(1,1,n,id[top[x]],id[x],z);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	update(1,1,n,id[x],id[y],z);
}
inline int qS(int x)
{
	res=0;
	query(1,1,n,id[x],id[x]+size[x]-1);
	return res;
} 
inline void updS(int x,int y)
{
	update(1,1,n,id[x],id[x]+size[x]-1,y);
}

//upd & query 

int main()
{
	n=read();m=read();r=read();p=read();
	for(int i=1;i<=n;++i)w[i]=read();
	for(int i=1;i<n;++i)
	{
		int u,v;u=read();v=read();
		add_edge(u,v);add_edge(v,u);
	}
	dfs1(r,0,1);dfs2(r,r);build(1,1,n);
	while(m--)
	{
		int k,x,y,z;k=read();
		if(k==1)
		{
			x=read();y=read();z=read();
			updR(x,y,z);
		}
		else if(k==2)
			{
				x=read();y=read();
				write(qR(x,y));putchar('\n');
			}
			else if(k==3)
				{
					x=read();y=read();
					updS(x,y);
				}
				else
				{
					x=read();
					write(qS(x));putchar('\n');
				}
	}
	return 0;
}

例2:Luogu P3379 【模板】最近公共祖先(LCA)

用树剖求 LCA 应该也是很普遍的一种算法了,复杂度 \(O(n\log n)\)。常数应该会比倍增小一点吧。

#include <bits/stdc++.h>
#define Rint register int
using namespace std;
const int N=5e5+5;
int n,m,s;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0){x=-x;putchar('-');}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}

//fread & fwrite

struct node{int to,nxt;}e[N<<1];
int head[N],cnt;
inline void add_edge(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

//addedge 

int dep[N],fa[N],size[N],son[N];
int wt[N],id[N],top[N],cnt1;
inline void dfs1(int u,int f,int deep)
{
	dep[u]=deep;fa[u]=f;size[u]=1;
	int hson=-1;
	for(Rint i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==f)continue;
		dfs1(v,u,deep+1);
		size[u]+=size[v];
		if(size[v]>hson)hson=size[v],son[u]=v;
	}
}
inline void dfs2(int u,int topf)
{
	id[u]=++cnt1;top[u]=topf;
	if(!son[u])return;
	dfs2(son[u],topf);
	for(Rint i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}

//dfs

inline int LCA(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}

//LCA

int main()
{
	n=read();m=read();s=read();
	for(Rint i=1;i<n;++i)
	{
		int u,v;u=read();v=read();
		add_edge(u,v);add_edge(v,u);
	}
	dfs1(s,0,1);dfs2(s,s);
	while(m--)
	{
		int u,v;u=read();v=read();
		write(LCA(u,v));putchar('\n');
	}
	return 0;
}

例3:Luogu P4114 Qtree1

又是一道比较板的树剖题。可以把边权挂在下面的点上,单点修改,区间查询,还是可以线段树维护。但是 LCA 位置需要特殊处理一下。以为 LCA 上的边权是上面的。t.c.\(O(n\log^2n)\)

#include <bits/stdc++.h>
#define Rint register int
#define ls rt<<1
#define rs rt<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+5; 
int n,st[N],ed[N];

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0){x=-x;putchar('-');}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}

//fread & fwrite

struct node{int to,nxt,len;}e[N<<1];
int head[N],cnt;
inline void add_edge(int u,int v,int wi)
{
	e[++cnt].to=v;e[cnt].nxt=head[u];
	e[cnt].len=wi;head[u]=cnt;
}

//addedge

int size[N],son[N],fa[N],dep[N],id[N],top[N],w[N],wt[N],cnt1;
inline void dfs1(int u,int f,int deep)
{
	dep[u]=deep;fa[u]=f;size[u]=1;
	int hson=-1;
	for(Rint i=head[u];i;i=e[i].nxt)
	{
		node v=e[i];
		if(v.to==f)continue;
		w[v.to]=v.len;
		dfs1(v.to,u,deep+1);
		size[u]+=size[v.to];
		if(size[v.to]>hson)hson=size[v.to],son[u]=v.to;
	}
}
inline void dfs2(int u,int topf)
{
	id[u]=++cnt1;wt[cnt1]=w[u];top[u]=topf;
	if(!son[u])return;
	dfs2(son[u],topf);
	for(Rint i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}

//dfs

int laz[N<<2],a[N<<2];
inline void build(int rt,int l,int r)
{
	if(l==r){a[rt]=wt[l];return;}
	build(ls,l,mid);build(rs,mid+1,r);
	a[rt]=max(a[ls],a[rs]);
}
inline int query(int rt,int l,int r,int L,int R)
{
	int res=-1;
	if(L<=l&&r<=R)return a[rt];
	if(L<=mid)res=max(res,query(ls,l,mid,L,R));
	if(R>mid)res=max(res,query(rs,mid+1,r,L,R));
	return res;
}
inline void update(int rt,int l,int r,int L,int R,int k)
{
	if(L<=l&&r<=R){a[rt]=k;return;}
	if(L<=mid)update(ls,l,mid,L,R,k);
	if(R>mid)update(rs,mid+1,r,L,R,k);
	a[rt]=max(a[ls],a[rs]);
}

//Segment Tree

inline int qM(int u,int v)
{
	int res=-1;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		res=max(res,query(1,1,n,id[top[u]],id[u]));
		u=fa[top[u]];
	}
	if(dep[u]>dep[v])swap(u,v);
	res=max(res,query(1,1,n,id[u]+1,id[v]));
	return res;
}
int main()
{
	n=read();
	for(Rint i=1;i<n;++i)
	{
		int wi;st[i]=read();ed[i]=read();wi=read();
		add_edge(st[i],ed[i],wi);add_edge(ed[i],st[i],wi);
	}
	dfs1(1,0,1);dfs2(1,1);build(1,1,n);
	while(1)
	{
		string s;cin>>s;
		if(s[0]=='D')break;
		int u,v;u=read();v=read();
		if(s[0]=='C')
		{
			if(dep[st[u]]<dep[ed[u]])u=ed[u];
			else u=st[u];
			update(1,1,n,id[u],id[u],v);
		}
		else
		{
			if(u==v)putchar('0');
			else write(qM(u,v));
			putchar('\n');
		}
	}
	return 0;
}
posted @ 2022-07-19 21:19  lnwhl  阅读(36)  评论(0)    收藏  举报