树链剖分
搞了好几天终于把树剖模板过了
感觉最难理解的是对两点之间路径上的修改,我就是在这里被卡了好久
还是看注释吧
原题链接:https://www.luogu.org/problemnew/show/P3384
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define sw(x,y) jzm=x,x=y,y=jzm;
#define For(i,j,k) for(int i=j;i<=k;++i)
using namespace std;
int read(){
int x=0,l=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') l=-1; ch=getchar();}
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
return x*l;
}
typedef long long ll;
const int N=100005;
struct node{
ll w,en;
}a[N*3];
int jzm,e,b[N],head[N],to[N<<1],nex[N<<1],mp[N],mp2[N];
void ad(int x,int y){
to[++e]=y; nex[e]=head[x]; head[x]=e;
}
void bt(int x,int l,int r){
if (l==r) {a[x].w=b[mp2[l]]; return;}
bt(x<<1,l,mid); bt(x<<1|1,mid+1,r);
a[x].w=a[x<<1].w+a[x<<1|1].w;
}
int xx,yy,n,m,r,p; ll k;
void pd(int fl,int l,int r){
a[fl<<1].w+=a[fl].en*(mid-l+1);
a[fl<<1|1].w+=a[fl].en*(r-mid);
a[fl<<1].en+=a[fl].en;
a[fl<<1|1].en+=a[fl].en;
a[fl].en=0;
}
void add(int fl,int l,int r){
if (l>=xx&&r<=yy){
a[fl].w+=k*(r-l+1); a[fl].en+=k; return;
}
if (a[fl].en) pd(fl,l,r);
if (mid>=xx) add(fl<<1,l,mid);
if (mid<yy) add(fl<<1|1,mid+1,r);
a[fl].w=a[fl<<1].w+a[fl<<1|1].w;
}
ll qu(int fl,int l,int r){
if (l>=xx&&r<=yy) return a[fl].w;
ll an=0;
if (a[fl].en) pd(fl,l,r);
if (mid>=xx) an+=qu(fl<<1,l,mid);
if (mid<yy) an+=qu(fl<<1|1,mid+1,r);
return an;
}
int cnt,siz[N],son[N],top[N],dep[N],fa[N];
void dfs1(int x,int de,int f){
siz[x]=1; dep[x]=de++; fa[x]=f; int ma=0;
for(int i=head[x];i;i=nex[i])
if (to[i]!=f){
dfs1(to[i],de,x);
if (ma<siz[to[i]]) ma=siz[to[i]],son[x]=to[i];
siz[x]+=siz[to[i]];
}
}
void dfs2(int x,int tp){
mp[x]=++cnt; top[x]=tp;
if (son[x]){
dfs2(son[x],tp);
for(int i=head[x];i;i=nex[i])
if (to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
}
}
int main(){
int x,y,fl,hk,wal,lca,de,f; ll ans; n=read(),m=read(),r=read(),p=read();
For(i,1,n) b[i]=read();
For(i,1,n-1){
x=read(),y=read();
ad(x,y); ad(y,x);
}
dfs1(r,1,0); dfs2(r,r);
For(i,1,n) mp2[mp[i]]=i; bt(1,1,n);
For(i,1,m){
fl=read(),x=read();
if (fl==3) xx=mp[x],yy=xx+siz[x]-1,k=read(),add(1,1,n);
else if (fl==4) xx=mp[x],yy=xx+siz[x]-1,printf("%lld\n",qu(1,1,n)%p);
else if (fl==2){
y=read(); ans=0;
while(top[x]!=top[y]){//当两个点不在同一条链上
if(dep[top[x]]<dep[top[y]]) sw(x,y);//把x点改为所在链顶端的深度更深的那个点
xx=mp[top[x]]; yy=mp[x]; ans+=qu(1,1,n);//ans加上x点到x所在链顶端 这一段区间的点权和
x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
}//直到两个点处于一条链上
if(dep[x]>dep[y]) sw(x,y);//把x点变为深度更浅的那个点
xx=mp[x]; yy=mp[y]; ans+=qu(1,1,n);//这时再加上此时两个点的区间和即可
printf("%lld\n",ans%p);
}
//接下来同上
else{
y=read(); k=read();
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) sw(x,y);
xx=mp[top[x]]; yy=mp[x]; add(1,1,n);
x=fa[top[x]];
}
if(dep[x]>dep[y]) sw(x,y);
xx=mp[x]; yy=mp[y]; add(1,1,n);
}
}
return 0;
}