点击查看折叠代码块
/*
树链剖分——重链剖分
可以求LCA
用线段树可以在树上维护区间最值、求区间和、求子树和等
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node{
int v,next;
}e[maxn<<1];
int head[maxn],cnt=0,ct=0;
int size[maxn];//子树的节点大小
int son[maxn];//重儿子
int top[maxn];//链头
int fa[maxn];//父亲节点
int d[maxn];//深度
int id[maxn];//节点的新标号
int sum[maxn<<2],lazy[maxn<<2],a[maxn],w[maxn];
int mode,n,m,s;
void add(int u,int v){
e[++cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs1(int x,int f,int deep){
size[x]=1;
d[x]=deep;
fa[x]=f;
int maxson=-1;
for (int i=head[x];i;i=e[i].next){
int v=e[i].v;
if(v!=f){
fa[v]=x;
dfs1(v,x,deep+1);
size[x]+=size[v];
if(size[v]>maxson){
son[x]=v;
maxson=size[v];
}
}
}
}
void dfs2(int x,int tp){
id[x]=++ct;
w[ct]=a[x];//将原节点的信息复制
top[x]=tp;
if(son[x]) dfs2(son[x],tp);//存在重儿子
for (int i=head[x];i;i=e[i].next){
int v=e[i].v;
if(v!=fa[x] && v!=son[x]){//不是重儿子的其他节点,把自己做为链头
dfs2(v,v);
}
}
}
int lca(int x,int y){//求LCA
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);//保证x是所在链的链头深度较大的那个
x=fa[top[x]];
}
return d[x]<d[y]?x:y;
}
//以下是线段树
void pushdown(int l,int r,int rt){
if(lazy[rt]){
int m=(l+r)>>1;
int d=lazy[rt];
lazy[rt<<1]+=d;
lazy[rt<<1|1]+=d;
sum[rt<<1]=(sum[rt<<1]+(m-l+1)*d) % mode;
sum[rt<<1|1]=(sum[rt<<1|1]+(r-m)*d) % mode;
lazy[rt]=0;
}
}
void pushup(int rt){
sum[rt]=(sum[rt<<1]+sum[rt<<1|1]) % mode;
}
void build(int l,int r,int rt){
if(l==r){
sum[rt]=w[l];
if(sum[rt]>mode) sum[rt] %=mode;
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
void update(int L,int R,int v,int l,int r,int rt){
if(L<=l && r<=R){
lazy[rt]=(lazy[rt]+v) % mode;
sum[rt]=(sum[rt]+(r-l+1)*v) % mode;
return ;
}
pushdown(l,r,rt);
int m=(l+r)>>1;
if(L<=m) update(L,R,v,l,m,rt<<1);
if(R>m) update(L,R,v,m+1,r,rt<<1|1);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l && r<=R){
return sum[rt] % mode;
}
pushdown(l,r,rt);
int m=(l+r)>>1;
int ans=0;
if(L<=m) ans=(ans+query(L,R,l,m,rt<<1)) % mode;
if(R>m) ans=(ans+query(L,R,m+1,r,rt<<1|1)) % mode;
return ans;
}
/*线段树单点查询这里用不到
int querypos(int pos,int l,int r,int rt){
if(l==r){
return sum[rt];
}
pushdown(l,r,rt);
int m=(l+r)>>1;
if(pos<=m) querypos(pos,l,m,rt<<1);
else querypos(pos,m+1,r,rt<<1|1);
}
*/
int Qdis(int x,int y){//操作2
int ans=0;
while(top[x]!=top[y]){//当两个点不在同一条链上
if(d[top[x]]<d[top[y]]) swap(x,y);//把x点改为所在链顶端的深度更深的那个点
ans+=query(id[top[x]],id[x],1,n,1);
ans%=mode;
x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
}
//直到两个点处于一条链上
if(d[x]>d[y]) swap(x,y);//把x点改为深度较小的那个点
ans+=query(id[x],id[y],1,n,1);//这时再加上此时两个点的区间和即可
return ans%=mode;
}
void Updis(int x,int y,int k){//操作1
k%=mode;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
update(id[top[x]],id[x],k,1,n,1);
x=fa[top[x]];
}
if(d[x]>d[y]) swap(x,y);
update(id[x],id[y],k,1,n,1);
}
int Qnode(int x){//操作4
return query(id[x],id[x]+size[x]-1,1,n,1) % mode;//子树区间右端点为id[x]+size[x]-1
}
void Upnode(int x,int k){//操作3
update(id[x],id[x]+size[x]-1,k,1,n,1);
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&mode);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for (int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs1(s,0,1);dfs2(s,s);build(1,n,1);//预处理
//询问
for (int i=1;i<=m;i++){
int op;
scanf("%d",&op);
if(op==1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Updis(x,y,z);
}
else if(op==2){
int x,y;
scanf("%d%d",&x,&y);
int ans=Qdis(x,y);
printf("%d\n",ans);
}
else if(op==3){
int x,z;
scanf("%d%d",&x,&z);
Upnode(x,z);
}
else if(op==4){
int x;
scanf("%d",&x);
int ans=Qnode(x);
printf("%d\n",ans);
}
}
return 0;
}