重链剖分 学习笔记

最好先看一遍OIwiki。

重链剖分是树链剖分的一种,其他的还有长链剖分等。

主要的思想是将树拆成许多链,对于每个链分别维护信息,然后合并信息以解决一些树上问题。

其中重链剖分应该是最常见的。

主要讲解重链剖分+线段树的这类题目。


剖分

首先定义:

重儿子:子树大小最大的子节点

轻儿子:除重儿子外的其他子节点

重边:与重儿子间的边

轻边:与轻儿子间的边

重链:重边连成的链

轻链:轻边连成的链

首先要求重儿子和轻儿子,我们来一遍dfs。

这次dfs负责求出节点的父亲,节点子树大小,深度及重儿子。

接下来再来一次dfs,求出每个节点在数据结构中的新编号,所在重链深度最小的节点(链头)。

重点

dfs时,首先要遍历重儿子在遍历其它儿子。

至于为什么,可以先看下文。


维护:

如果遇到一道题,支持树上点权修改,链上点权最小值。

单点修改,区间查询,很容易想到线段树

但是线段树时维护一段连续的区间,这里不连续,怎么办?

我们可以通过上文的第二遍dfs来重编号,使每条重链的新编号连续。

这样一个链上的信息即可由许多重链的信息合并得到。

如果同时还要维护子树上的信息呢?

这样dfs可以保证子树的编号连续。

所以就可以愉快地使用线段树进行维护了。

子树修改,链修改同理。


例题:

洛谷 P3384 【模板】轻重链剖分

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];
#define ls (x<<1)
#define rs (x<<1|1)
#define rep(i,a,b) for(register int i=a;i<=b;++i)
#define Rep(i,a,b) for(register int i=a;i>=b;--i)
inline int read()
{
	int x=0;bool f=0;char ch;
	do{ch=getchar();f|=(ch=='-');}while(!isdigit(ch));
	do{x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}while(isdigit(ch));
	return f?-x:x;
}
inline void write(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x>9)write(x/10);putchar(x%10+'0');
}
inline void writeln(int x)
{
	write(x);puts("");
}
int mod;
namespace segtree
{
	struct node
	{
		int ans;
		int tag;
	}tree[maxn<<2];
	inline void f(int x,int l,int r,int k)
	{
		(tree[x].tag+=k)%=mod;
		(tree[x].ans+=k*(r-l+1))%=mod;
	}
	inline void push_up(int x)
	{
		tree[x].ans=(tree[ls].ans+tree[rs].ans)%mod;
	}
	inline void push_down(int x,int l,int r)
	{
		int mid=(l+r)>>1;
		f(ls,l,mid,tree[x].tag);
		f(rs,mid+1,r,tree[x].tag);
		tree[x].tag=0;
	}
	void build(int x,int l,int r)
	{
		if(l==r){tree[x].ans=a[l]%mod;return;}
		int mid=(l+r)>>1;
		build(ls,l,mid);build(rs,mid+1,r);
		push_up(x);
	}
	void modify(int x,int l,int r,int ql,int qr,int k)
	{	
		if(ql<=l&&r<=qr){f(x,l,r,k);return;}
		int mid=(l+r)>>1;
		push_down(x,l,r);
		if(ql<=mid)modify(ls,l,mid,ql,qr,k);
		if(mid<qr)modify(rs,mid+1,r,ql,qr,k);
		push_up(x);
	}
	int query(int x,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr){return tree[x].ans%mod;}
		int res=0;
		int mid=(l+r)>>1;
		push_down(x,l,r);
		if(ql<=mid)(res+=query(ls,l,mid,ql,qr))%=mod;
		if(mid<qr)(res+=query(rs,mid+1,r,ql,qr))%=mod;
		return res%mod;
	}
}
int n,m,r;
namespace tree
{
	int head[maxn],nxt[maxn<<1],to[maxn<<1],tot=0;
	inline void add(int u,int v)
	{
		nxt[++tot]=head[u];
		head[u]=tot;
		to[tot]=v;
	}
	int fa[maxn],siz[maxn],top[maxn],son[maxn],dep[maxn],id[maxn],rk[maxn],val[maxn],cnt=0;
	inline void dfs1(int u,int anc)
	{
		siz[u]=1;fa[u]=anc;dep[u]=dep[anc]+1;
		for(register int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(v==anc)continue;
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
	inline void dfs2(int u,int topx)
	{
		id[u]=++cnt;rk[cnt]=u;top[u]=topx;
		a[id[u]]=val[u];
		if(son[u])dfs2(son[u],topx);
		for(register int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(v==fa[u]||v==son[u])continue;
			dfs2(v,v);
		}
	}
	inline void rmodi(int u,int v,int k)
	{
		int ans=0;
		while(top[u]!=top[v])
		{	if(dep[top[u]]<dep[top[v]]) swap(u,v);
			segtree::modify(1,1,n,id[top[u]],id[u],k);
			u=fa[top[u]];
		}
		if(dep[u]>dep[v])swap(u,v);
		segtree::modify(1,1,n,id[u],id[v],k);
	}
	inline int rsum(int u,int v)
	{
		int ans=0;
		while(top[u]!=top[v])
		{	
			if(dep[top[u]]<dep[top[v]]) swap(u,v);
			(ans+=segtree::query(1,1,n,id[top[u]],id[u]))%=mod;
			u=fa[top[u]];
			
		}
		if(dep[u]>dep[v])swap(u,v);
		(ans+=segtree::query(1,1,n,id[u],id[v]))%=mod;
		return ans;
	}
	inline void tmodi(int x,int k)
	{
		segtree::modify(1,1,n,id[x],id[x]+siz[x]-1,k%mod);
	}
	inline int tsum(int x)
	{
		return segtree::query(1,1,n,id[x],id[x]+siz[x]-1)%mod;
	}
}
int main()
{
	n=read(),m=read(),r=read();mod=read();
	rep(i,1,n)tree::val[i]=read();
	rep(i,1,n-1)
	{
		int u=read(),v=read();
		tree::add(u,v);tree::add(v,u);
	}
	tree::dfs1(r,0);tree::dfs2(r,r);

	segtree::build(1,1,n);
	while(m--)
	{
		int op=read();
		if(op==1)
		{
			int x=read(),y=read(),z=read()%mod;
			tree::rmodi(x,y,z);
		}
		else if(op==2)
		{
			int x=read(),y=read();
			writeln(tree::rsum(x,y)%mod);
		}
		else if(op==3)
		{
			int x=read(),z=read()%mod;
			tree::tmodi(x,z);
		}
		else
		{
			int x=read();
			writeln(tree::tsum(x)%mod);
		}
	}
	return 0;
}
posted @ 2020-08-23 17:10  ฅ(OωO)ฅ  阅读(113)  评论(0)    收藏  举报
"scale": 1 }, "display": { "position": "left", "width": 100, "height": 200, "hOffset": 70, "vOffset": 0 }, "mobile": { "show": true, "scale": 0.5 }, "react": { "opacityDefault": 0.7, "opacityOnHover": 0.2 } }); window.onload = function(){ $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;") } -->