本来这个早该学了的,去luogu题解区学了一番,发现代码比LCT还长,当然还是要好写些。

概念:

  • 重儿子:子节点重sz最大的儿子,否则是轻儿子
    ps.一个点可以作为重儿子就叫它重节点了,反之轻节点。
  • 重链:由一个轻节点开始然后是重节点,直到叶子。
  • 轻边:由一个存在轻节点的边。
  • 性质:树上一条链上最多,\(log_2(n)\)条轻边,所以我们可以拆成多个重链(用数据结构维护),然后轻边时手动更新

流程:

  • 找到son[](重儿子)
  • 找到top,同是存下dfn[]
  • 然后映射到i->dfn[i],构建数据结构(线段树)。
  • 处理询问,(如果是链的话,如暴力求lca网上跳一样,不过是log次的)
  • code: P3384
#include<bits/stdc++.h>
using namespace std;
#define ls(x) x<<1
#define rs(x) x<<1|1
typedef long long ll;
const int N=1e6+5;
int b[N],fa[N],n,m,rt,mod,nxt[N],to[N],head[N],ecnt;
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
ll a[N];
struct seg {int l,r,len;ll sum,lazy;}T[N];
void P_up(int x) {T[x].sum=T[ls(x)].sum+T[rs(x)].sum;}
void P_dw(int x) {
	if(!T[x].lazy)return;
	int ls=ls(x),rs=rs(x);
	T[ls].sum+=T[ls].len*T[x].lazy,T[rs].sum+=T[rs].len*T[x].lazy,T[ls].lazy+=T[x].lazy,T[rs].lazy+=T[x].lazy;
	T[ls].sum%=mod,T[rs].sum%=mod,T[ls].lazy%=mod,T[rs].lazy%=mod;T[x].lazy=0;
}
void Build(int x,int l,int r) {
	T[x]=(seg){l,r,r-l+1,0,0};
	if(l==r) {T[x].sum=b[l];return;}
	int mid=(l+r)>>1;
	Build(ls(x),l,mid);Build(rs(x),mid+1,r);
	P_up(x);
//	printf("%d[%d,%d]: %lld\n",x,l,r,T[x].sum);
}
void Update(int x,int l,int r,ll w) {
	if(l<=T[x].l&&T[x].r<=r) {T[x].sum=(T[x].sum+w*T[x].len)%mod;T[x].lazy=(T[x].lazy+w)%mod;return;}
	P_dw(x);
	int mid=(T[x].l+T[x].r)>>1;
	if(l<=mid)Update(ls(x),l,r,w);
	if(r>mid)Update(rs(x),l,r,w);
	P_up(x);
}
ll Query(int x,int l,int r) {
	if(l<=T[x].l&&T[x].r<=r) {return T[x].sum;}
	P_dw(x);
	int mid=(T[x].l+T[x].r)>>1;ll res=0;
	if(l<=mid) res+=Query(ls(x),l,r);
	if(r>mid) res+=Query(rs(x),l,r);
	return res%mod;
}
int sz[N],son[N],top[N],dfn[N],Time,dep[N];
void gt_son(int x) {
	sz[x]=1;
	for(int i=head[x];i;i=nxt[i]) {
		int y=to[i];if(y==fa[x])continue;
		dep[y]=dep[x]+1;fa[y]=x;
		gt_son(y);sz[x]+=sz[y];
		if(sz[son[x]]<sz[y])son[x]=y;
	}
}
void gt_top(int x,int Tp) {
	top[x]=Tp;dfn[x]=++Time;b[Time]=a[x];
	if(son[x])gt_top(son[x],Tp);
	for(int i=head[x];i;i=nxt[i]) {
		int y=to[i];
		if(y==fa[x]||y==son[x])continue;
		gt_top(y,y);
	}
}
void ask_lian(int x,int y) {
	ll res=0;
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=(res+Query(1,dfn[top[x]],dfn[x]))%mod;x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	res=(res+Query(1,dfn[x],dfn[y]))%mod;
	printf("%lld\n",res);
}
void add_lian(int x,int y,ll w) {
	while(top[x]!=top[y]) {
//		printf("!%d %d\n",x,y);
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		Update(1,dfn[top[x]],dfn[x],w);x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	Update(1,dfn[x],dfn[y],w);
}
int main() {
	scanf("%d%d%d%d",&n,&m,&rt,&mod);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);add_edge(u,v),add_edge(v,u);}
	gt_son(rt);gt_top(rt,rt);
	Build(1,1,n);
	for(int i=1;i<=m;i++) {
		int opt,x,y;ll z;
		scanf("%d",&opt);
		if(opt==1) {scanf("%d%d%lld",&x,&y,&z);add_lian(x,y,z);}
		else if(opt==2) {scanf("%d%d",&x,&y);ask_lian(x,y);}
		else if(opt==3) {scanf("%d%lld",&x,&z);Update(1,dfn[x],dfn[x]+sz[x]-1,z);}
		else {scanf("%d",&x);printf("%lld\n",Query(1,dfn[x],dfn[x]+sz[x]-1));}
	}
	return 0;
}