题目链接
题目大意:
已知一棵包含 N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
1、 x y z,表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z。
2、 x y,表示求树从 x 到 y 结点最短路径上所有节点的值之和。
3、 x z,表示将以 x 为根节点的子树内所有节点值都加上 z。
4、 x 表示求以 x 为根节点的子树内所有节点值之和。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,r,p;
int ch,x,y,z;
int a[100005];
int h[100005],e[100005*2],ne[100005*2];
int inx,idx;
int d[100005],size[100005],top[100005],f[100005],son[100005];
//d数组表示一个点的深度,size[i]表示以i为根节点的子树的点的数量
//top[i]求i所在重链的第一个节点,f表示一个节点的父节点
//son记录以子节点为根的所有子树中节点数量最多的子节点
int s[100005],dfsxu[100005],ni[100005];//DFS序列的三个相关数组
struct ty{
int l,r;
ll lazy;
ll sum;
ty():l(0),r(0),lazy(0),sum(0){ }
};
ty tr[100005*4];
void add(int x,int y){//链式前向星
ne[inx]=h[x];e[inx]=y;h[x]=inx++;
return ;
}
void dfs1(int u,int fa){
d[u]=d[fa]+1; size[u]=1;
f[u]=fa; son[u]=0;
for(int i=h[u];i!=-1;i=ne[i]){
int to=e[i];
if(to==fa) continue;
dfs1(to,u);
size[u]+=size[to];
if(size[son[u]]<size[to]) son[u]=to;
}
return ;
}
void dfs2(int u,int tops){//求top数组
top[u]=tops;
s[u]=++idx;
dfsxu[idx]=u;
if(son[u]!=0) dfs2(son[u],tops);
for(int i=h[u];i!=-1;i=ne[i]){
int to=e[i];
if(to!=f[u]&&to!=son[u]){
dfs2(to,to);
}
}
ni[u]=idx;
return ;
}
//线段树基本操作
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
return ;
}
void build(int u,int l,int r){
tr[u].l=l; tr[u].r=r;
tr[u].lazy=0;
if(l==r) {
tr[u].sum=a[dfsxu[l]];
return ;
}
int mind=(l+r)>>1;
build(u<<1,l,mind);
build(u<<1|1,mind+1,r);
pushup(u);
return ;
}
void pushdown(int u){
if(tr[u].lazy){
tr[u<<1].sum+=(tr[u<<1].r-tr[u<<1].l+1)*tr[u].lazy;
tr[u<<1|1].sum+=(tr[u<<1|1].r-tr[u<<1|1].l+1)*tr[u].lazy;
tr[u<<1].lazy+=tr[u].lazy;
tr[u<<1|1].lazy+=tr[u].lazy;
tr[u].lazy=0;
}
return ;
}
void modify(int u,int l,int r,int da){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].sum+=(tr[u].r-tr[u].l+1)*da;
tr[u].lazy+=da;
return ;
}
pushdown(u);
int mind=(tr[u].l+tr[u].r)>>1;
if(l<=mind) modify(u<<1,l,r,da);
if(r>mind) modify(u<<1|1,l,r,da);
pushup(u);
return ;
}
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].sum;
}
pushdown(u);
int mind=(tr[u].l+tr[u].r)>>1;
ll ans=0;
if(l<=mind) ans+=query(u<<1,l,r);
if(r>mind) ans+=query(u<<1|1,l,r);
return ans;
}
void update1(int x,int y,int z){
int l,r;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
l=s[top[x]]; r=s[x];
modify(1,l,r,z);
x=f[top[x]];
//l=s[f[top[x]]]; r=s[top[x]];
//modify(1,l,r,z);
}
if(d[x]>d[y]) swap(x,y);
l=s[x]; r=s[y];
modify(1,l,r,z);
return ;
}
ll merge1(int x,int y){
int l,r;
ll ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) swap(x,y);
l=s[top[x]]; r=s[x];
ans+=query(1,l,r);
//l=s[f[top[x]]]; r=s[top[x]];
//ans+=query(1,l,r);注意注释掉的这一部分轻边是不需要算的,不然会重复计算一些点
x=f[top[x]];
}
if(d[x]>d[y]) swap(x,y);
l=s[x]; r=s[y];
//cout<<"l="<<l<<"r="<<r<<"\n";
ans+=query(1,l,r);
//cout<<"ans="<<query(1,l,r)<<"\n";
return ans;
}
void update2(int x,int z){
int l,r;
l=s[x]; r=ni[x];
modify(1,l,r,z);
return ;
}
ll merge2(int x){
int l,r;
l=s[x]; r=ni[x];
return query(1,l,r);
}
int main(){
memset(h,-1,sizeof(h));
scanf(" %d %d %d %d",&n,&m,&r,&p);
for(int i=1;i<=n;i++) scanf(" %d",&a[i]);
for(int i=1;i<n;i++){
scanf(" %d %d",&x,&y);
add(x,y); add(y,x);
}
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
for(int i=1;i<=m;i++){
scanf(" %d",&ch);
if(ch==1){
scanf(" %d %d %d",&x,&y,&z);
update1(x,y,z);
}
else if(ch==2){
scanf(" %d %d",&x,&y);
ll ans=merge1(x,y);
printf("%d\n",ans%p);
}
else if(ch==3){
scanf(" %d %d",&x,&z);
update2(x,z);
}
else{
scanf(" %d",&x);
ll ans=merge2(x);
printf("%d\n",ans%p);
}
}
return 0;
}
京公网安备 11010502036488号