本来这个早该学了的,去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;
}