P3384 【模板】轻重链剖分

H y p e r l i n k Hyperlink Hyperlink

https://www.luogu.com.cn/problem/P3384


D e s c r i p t i o n Description Description

给定一棵大小为 n n n的有点权的无向有根树,要求支持路径增值,子树增值以及它们的查询

数据范围: n ≤ 1 0 5 n\leq 10^5 n105


S o l u t i o n Solution Solution

树链剖分练习题,这里简述原理

找出每个节点的重儿子,链的顶端,子树大小

查询/修改 x x x y y y的路径,如果已经在同一条链上,直接计算,否则强令 d e p [ t o p [ x ] ] > d e p [ t o p [ y ] ] dep[top[x]]>dep[top[y]] dep[top[x]]>dep[top[y]],然后一直让 x x x跳到 f a [ t o p [ x ] ] fa[top[x]] fa[top[x]],中间计算/修改

子树的话即为 i d [ x ] ∼ i d [ x ] + s i z [ x ] − 1 id[x]\sim id[x]+siz[x]-1 id[x]id[x]+siz[x]1即可

时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)


C o d e Code Code

#include 
#include
#include
#define N 100010
#define LL long long
using namespace std;int n,m,root,mod,fa[N],l[N],tot,x,y,dep[N],siz[N],son[N],id[N],cnt,top[N],nval[N],opt;
struct node{int next,to;}e[N<<1];
inline void add(int u,int v){e[++tot]=(node){l[u],v};l[u]=tot;return;}
LL val[N],v;
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void dfs1(int x)
{
	siz[x]=1;
	int maxn=0; 
	for(register int i=l[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(y==fa[x]) continue;
		dep[y]=dep[x]+1;fa[y]=x;
		dfs1(y);
		siz[x]+=siz[y];
		if(siz[y]>maxn)	maxn=siz[son[x]=y];
	}
	return;
}
inline void dfs2(int x,int topf)
{
	id[x]=++cnt;
	top[x]=topf;
	nval[cnt]=val[x];
	if(son[x]==0) return;
	dfs2(son[x],topf);
	for(register int i=l[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(y==fa[x]||y==son[x]) continue;
		dfs2(y,y);
	}
	return;
}
struct xds
{
	#define lson k<<1
	#define rson k<<1|1
	LL sum[N<<2],lzy[N<<2];
	inline void pushdown(int k,int l,int r)
	{
		if(lzy[k]==0) return;
		int mid=l+r>>1;
		lzy[lson]+=lzy[k];lzy[rson]+=lzy[k];
		(sum[lson]+=(mid-l+1)*lzy[k]%mod)%=mod;(sum[rson]+=(r-mid)*lzy[k]%mod)%=mod;
		lzy[k]=0;
		return;
	}
	inline void pushup(int k)
	{
		sum[k]=(sum[lson]+sum[rson])%mod;
		return;
	}
	inline void build(int k=1,int l=1,int r=cnt)
	{
		if(l==r)
		{
			sum[k]=nval[l]%mod;
			return;
		}
		int mid=l+r>>1;
		build(lson,l,mid);build(rson,mid+1,r);
		pushup(k);
		return;
	}
	inline void Modify(int ql,int qr,LL v,int k=1,int l=1,int r=cnt)
	{
		if(ql<=l&&r<=qr) {lzy[k]+=v;sum[k]+=(r-l+1)*v%mod;return;}
		int mid=l+r>>1;
		pushdown(k,l,r);
		if(ql<=mid) Modify(ql,qr,v,lson,l,mid);
		if(qr>mid) Modify(ql,qr,v,rson,mid+1,r);
		pushup(k);
		return;
	}
	inline LL Ask(int ql,int qr,int k=1,int l=1,int r=cnt)
	{
		if(ql<=l&&r<=qr) return sum[k];
		LL res=0;int mid=l+r>>1;
		pushdown(k,l,r);
		if(ql<=mid) res+=Ask(ql,qr,lson,l,mid);
		if(qr>mid) res+=Ask(ql,qr,rson,mid+1,r);
		pushup(k);
		return res;
	}
	#undef lson
	#undef rson	
}T;
inline LL qlj(int x,int y)
{
	LL res=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		(res+=T.Ask(id[top[x]],id[x]))%=mod;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	(res+=T.Ask(id[x],id[y]))%=mod;
	return res;
}
inline LL qzs(int x){return T.Ask(id[x],id[x]+siz[x]-1);}
inline void mlj(int x,int y,LL v)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		T.Modify(id[top[x]],id[x],v);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	T.Modify(id[x],id[y],v);
	return;
}
inline void mzs(int x,LL v){T.Modify(id[x],id[x]+siz[x]-1,v);}
signed main()
{
	n=read();m=read();root=read();mod=read();
	for(register int i=1;i<=n;i++) val[i]=read();
	for(register int i=1;i<n;i++)
	{
		x=read();y=read();
		add(x,y);add(y,x);
	}
	dep[root]=1;
	dfs1(root);
	dfs2(root,root);
	T.build();
	while(m--)
	{
		opt=read();
		if(opt==1)
		{
			x=read();y=read();v=read();
			mlj(x,y,v);
		}
		if(opt==2)
		{
			x=read();y=read();
			printf("%lld\n",qlj(x,y)%mod);
		}
		if(opt==3)
		{
			x=read();v=read();
			mzs(x,v);
		}
		if(opt==4)
		{
			x=read();
			printf("%lld\n",qzs(x)%mod);
		}
	}
}

你可能感兴趣的:(树链剖分,P3384,轻重链剖分)