重链剖分学习笔记

前置:线段树(或对一棵树树剖之后的维护用到的其他数据结构)、dfs 序。

定义

  • 重儿子:子结点中子树大小最大的节点。
  • 轻儿子:除重儿子外的其他所有子节点。
  • 重边:非叶子节点到它的重儿子的边。
  • 轻边:非叶子节点到它的轻儿子的边。
  • 重链:若干条首尾相连接的重边构成的链。
  • 链首:每条重链中深度最小的点。

实现

是将整棵树分为若干个重链的过程。两个 dfs 求出 dfs 序、子树大小、重儿子和链首即可。

因为需要实现某一条重链的操作,这个地方的 dfs 序需要先遍历重儿子,这样就可以确保一条重链的编号是连续的。

void dfs1(int u,int fa)
{
	siz[u]=1;
	for(auto v:g[u])
	{
		if(v==fa) continue;
		dep[v]=dep[u]+1;
		da[v]=u;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int fa)
{
	dfn[u]=++num;
	if(son[u]) top[son[u]]=top[u],dfs2(son[u],u);
	for(auto v:g[u])
	{
		if(v==fa||v==son[u]) continue;
		top[v]=v;
		dfs2(v,u);
	}
}

然后单点子树查询修改都很简单,对于链 \((x,y)\) 的操作,考虑每次将其链首深度大的跳到链首,如果跳 \(x,y\) 深度大的就会出现将 \(x\) 跳到 \(fa_1\) 的情况,处理起来比较麻烦。

复杂度的证明:

不难发现每跳一次重链的链首,链首到其父亲的边是一条轻边。

设当前经过的点的子树大小为 \(x\),则若经过一条轻边,\(x\) 至少翻倍。

所以一共只会经过 \(O(\log)\) 条轻边,\(O(\log)\) 条重链。

模板

#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define dbg(x) sd cout<<#x<<":"<<x<<" "
#define dg(x) sd cout<<#x<<":"<<x<<"\n"
#define inf 1e10
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=5e5+10,P=1e9+7;
int n,dfn[N],num,w[N],siz[N],top[N],son[N],da[N],dep[N],a[N];//son为其重儿子
sd vector<int> g[N];
struct seg_Tree
{
	int s[N<<2],ma[N<<2];//维护和以及最大值
	#define ls k<<1
	#define rs k<<1|1
	void pushup(int k)
	{
		s[k]=s[ls]+s[rs];
		ma[k]=sd max(ma[ls],ma[rs]);
	}
	void built(int k,int l,int r)
	{
		if(l==r)
		{
			s[k]=ma[k]=a[l];
			return;
		}
		int mid=l+r>>1;
		built(ls,l,mid);
		built(rs,mid+1,r);
		pushup(k);
	}
	void update(int k,int l,int r,int x,int v)//单点修改
	{
		if(l==r)
		{
			a[l]=s[k]=ma[k]=v;
			return;
		}
		int mid=l+r>>1;
		if(x<=mid) update(ls,l,mid,x,v);
		else update(rs,mid+1,r,x,v);
		pushup(k);
	}
	pii ask(int k,int l,int r,int x,int y)
	{
		if(x<=l&&y>=r) return {s[k],ma[k]};
		int mid=l+r>>1;
		pii res={0,-inf},K;
		if(x<=mid)
		{
			K=ask(ls,l,mid,x,y);
			res={res.X+K.X,sd max(res.Y,K.Y)};
		}
		if(y>mid)
		{
			K=ask(rs,mid+1,r,x,y);
			res={res.X+K.X,sd max(res.Y,K.Y)};
		}
		return res;
	}
	pii query(int x,int y)
	{
		pii k,res={0,-inf};
		while(top[x]!=top[y])
		{
			if(dep[top[x]]<dep[top[y]]) sd swap(x,y);
			// dbg(top[x]),dg(x);
			k=ask(1,1,n,dfn[top[x]],dfn[x]);
			res={res.X+k.X,sd max(res.Y,k.Y)};
			x=da[top[x]];
		}
		if(dep[x]<dep[y]) sd swap(x,y);
		// dbg(y),dg(x);
		k=ask(1,1,n,dfn[y],dfn[x]);
		return {res.X+k.X,sd max(res.Y,k.Y)};
	}
}T;
void dfs1(int u,int fa)
{
	siz[u]=1;
	for(auto v:g[u])
	{
		if(v==fa) continue;
		dep[v]=dep[u]+1;
		da[v]=u;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int fa)
{
	dfn[u]=++num;
	if(son[u]) top[son[u]]=top[u],dfs2(son[u],u);
	for(auto v:g[u])
	{
		if(v==fa||v==son[u]) continue;
		top[v]=v;
		dfs2(v,u);
	}
}
void solve()
{
	n=read();
	F(i,2,n)
	{
		int x=read(),y=read();
		g[x].emplace_back(y);
		g[y].emplace_back(x);
	}
	F(i,1,n) w[i]=read();
	dep[1]=da[1]=1;
	dfs1(1,0);
	top[1]=1;
	dfs2(1,0);
	F(i,1,n) a[dfn[i]]=w[i];
	T.built(1,1,n);
	int q=read();
	while(q--)
	{
		char op[6];
		scanf("%s",op);
		int x=read(),y=read();
		if(op[0]=='C')
		{
			T.update(1,1,n,dfn[x],y);
		}
		if(op[0]=='Q')
		{
			pii res=T.query(x,y);
			if(op[1]=='M') put(res.Y);
			else put(res.X);
		}
	}
	
}
signed main()
{
// 	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int T=1;
	// T=read();
	while(T--) solve();
    return 0;
}
posted @ 2024-12-04 21:29  _E_M_T  阅读(35)  评论(0)    收藏  举报