题目
时间:1200ms。
核心是将一颗树转换为线段树。
首先两次dfs,第一次求出节点的重儿子,父亲节点,深度,以及以此节点为树的子树大小,第二次则求出在重链中的顶点,树中位置,此时树已转换成链,建线段树,后对于o=3与o=4用线段树可做,至于当o=1或o=2时,是一个用top数组优化的暴力lca,在暴力的过程中用线段树的方法辅助即可。
#include<bits/stdc++.h>
using namespace std;
long long n,m,p,r,ver[200001],nxt[200001],hed[100001],tot=0,al[200001],f[100001]
int d[100001],rk[200001],id[200001],size[100001],top[100001],son[100001];
//ver,nxt,hed 邻接链表 al 节点值 f 父节点 d 深度 rk 记录在树中的位置
//id 记录编号 size 子树大小 top 重链顶点 son 重儿子
struct NODE{
long long l,r,ls,rs,lazy,sum;
}node[200001];
void add(long long x,long long y){ ver[++tot]=y,nxt[tot]=hed[x],hed[x]=tot;}
void dfs1(long long x,long long y){
f[x]=y,d[x]=d[y]+1;
size[x]=1;
for(long long i=hed[x];i;i=nxt[i]){
if(ver[i]!=y){
dfs1(ver[i],x);
size[x]+=size[ver[i]];
if(size[son[x]]<size[ver[i]]) son[x]=ver[i];
}
}
}
void dfs2(long long x,long long y){
top[x]=y,id[x]=++tot,rk[tot]=x;
if(!son[x]) return;
dfs2(son[x],y);
for(long long i=hed[x];i;i=nxt[i]){
if((ver[i]!=son[x])&&(ver[i]!=f[x])) dfs2(ver[i],ver[i]);
}
}
void pushup(long long x){
node[x].sum=(node[node[x].ls].sum+node[node[x].rs].sum+node[x].lazy*(node[x].r-node[x].l+1))%p;
}
void build(long long x,long long y,long long z){
if(y==z){
node[x].l=node[x].r=y;
node[x].sum=al[rk[y]];
return;
}
long long mid=(y+z)/2;
node[x].ls=tot++,node[x].rs=tot++;
build(node[x].ls,y,mid),build(node[x].rs,mid+1,z);
node[x].l=node[node[x].ls].l,node[x].r=node[node[x].rs].r;
pushup(x);
}
long long query(long long x,long long y,long long z){
if(y<=node[x].l&&node[x].r<=z) return node[x].sum;
long long ans=node[x].lazy*(min(node[x].r,z)-max(node[x].l,y)+1)%p,mid=(node[x].l+node[x].r)/2;
if(y<=mid) ans=(ans+query(node[x].ls,y,z))%p;
if(mid<z) ans=(ans+query(node[x].rs,y,z))%p;
return ans%p;
}
long long sum(long long x,long long y){
long long ans=0,fx=top[x],fy=top[y];
while(fx!=fy){
if(d[fx]>=d[fy]){
(ans+=query(0,id[fx],id[x]))%=p;
x=f[fx],fx=top[x];
}else{
ans=(ans+query(0,id[fy],id[y]))%p;
y=f[fy],fy=top[y];
}
}
if(id[x]<=id[y]) ans=(ans+query(0,id[x],id[y]))%p;
else ans=(ans+query(0,id[y],id[x]))%p;
return ans%p;
}
void update(long long rt,long long x,long long y,long long z){
if(x<=node[rt].l&&node[rt].r<=y){
node[rt].sum=(node[rt].sum+z*(node[rt].r-node[rt].l+1))%p;
node[rt].lazy=(node[rt].lazy+z)%p;
return;
}
long long mid=(node[rt].l+node[rt].r)/2;
if(x<=mid) update(node[rt].ls,x,y,z);
if(mid<y) update(node[rt].rs,x,y,z);
pushup(rt);
}
void updates(long long x,long long y,long long z){
long long fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]>=d[fy]){
update(0,id[fx],id[x],z);
x=f[fx],fx=top[x];
}
else {
update(0,id[fy],id[y],z);
y=f[fy],fy=top[y];
}
}
if(id[x]<=id[y]) update(0,id[x],id[y],z);
else update(0,id[y],id[x],z);
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
for(long long i=1;i<=n;i++) scanf("%lld",&al[i]);
for(long long i=1,x,y;i<n;i++){
scanf("%lld%lld",&x,&y);
add(x,y),add(y,x);
}
tot=0;
dfs1(r,0),dfs2(r,r);
tot=1;
build(0,1,n);
for(long long i=1,o,x,y,z;i<=m;i++){
scanf("%lld",&o);
if(o==1){
scanf("%lld%lld%lld",&x,&y,&z);
updates(x,y,z);
}
if(o==2){
scanf("%lld%lld",&x,&y);
printf("%lld\n",sum(x,y));
}
if(o==3){
scanf("%lld%lld",&x,&y);
update(0,id[x],id[x]+size[x]-1,y);
}
if(o==4){
scanf("%lld",&x);
printf("%lld\n",query(0,id[x],id[x]+size[x]-1));
}
}
return 0;
}