注释还算比较详细。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=100000+10;
const int maxm=100000+10;
struct Edge // 链式前向星存图
{
int before;
int to;
}e[maxm<<1];
int n,m,r,mod,k,head[maxn];
int tim=0; // 时间戳
int temp_val[maxn]; // 暂时记录当前节点权值
int val[maxn]; // 记录当前dfs序结点的权值
int size[maxn];// 以当前结点为根的树的大小
int top[maxn]; // 当前重链的链头
int son[maxn]; // 记录当前节点的重儿子是谁
int fa[maxn];// 当前节点的父节点
int dep[maxn]; // 当前结点的深度
int ID[maxn]; // 结点新编号 即 dfs序
int tree[maxn<<2],tag[maxn<<2]; // 存储线段树和懒惰标记
void add(int u,int v)
{
e[k].before=head[u];
e[k].to=v;
head[u]=k++;
}
void dfs1(int u,int f)
{
fa[u]=f; // 记录父节点
size[u]=1; // 初始树大小为1
int max_son=-1; // 用来求重儿子
for(int i=head[u];i!=-1;i=e[i].before)
{
int v=e[i].to;
if(v==f)
continue; // 不能回去
dep[v]=dep[u]+1;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>max_son)
{
max_son=size[v];
son[u]=v; // 记录重儿子
}
}
}
void dfs2(int u,int topf)
{
ID[u]=++tim; // 节点的dfs序
val[tim]=temp_val[u]; // 将暂时记录的结点权值赋新的结点
top[u]=topf; // 记录当前点的链头
if(!son[u])
return; // 如果没有儿子,那么返回
dfs2(son[u],topf);
for(int i=head[u];i!=-1;i=e[i].before)
{
int v=e[i].to;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v); // 从每一个轻儿子开始都是一条链
}
}
void pushdown(int node,int lenL,int lenR)
{
if(tag[node])
{
tag[node<<1]+=tag[node];
tag[node<<1|1]+=tag[node];
tree[node<<1]+=lenL*tag[node];
tree[node<<1|1]+=lenR*tag[node];
tag[node]=0;
}
}
void creat(int node,int start,int end)
{
if(start==end)
{
tree[node]=val[end];
if(tree[node]>mod)
{
tree[node]%=mod;
}
return;
}
int mid=(start+end)>>1;
creat(node<<1,start,mid);
creat(node<<1|1,mid+1,end);
tree[node]=(tree[node<<1]%mod+tree[node<<1|1]%mod)%mod;
}
void update(int node,int start,int end,int L,int R,int z) // 区间加 + z
{
if(L>end||R<start)
{
return;
}
if(L<=start&&R>=end)
{
tree[node]+=(end-start+1)*z;
tag[node]+=z;
return;
}
int mid=(start+end)>>1;
pushdown(node,mid-start+1,end-mid);
if(L<=mid)
update(node<<1,start,mid,L,R,z);
if(R>=mid+1)
update(node<<1|1,mid+1,end,L,R,z);
tree[node]=(tree[node<<1]%mod+tree[node<<1|1]%mod)%mod;
}
int query(int node,int start,int end,int L,int R) // L, R 为查询区间
{
if(start>R||end<L)
{
return 0;
}
if(start>=L&&end<=R)
{
return tree[node];
}
else
{
int mid=(start+end)>>1;
pushdown(node,mid-start+1,end-mid);
int lans=0;
int rans=0;
if(mid>=L)
lans=query(node<<1,start,mid,L,R);
if(mid+1<=R)
rans=query(node<<1|1,mid+1,end,L,R);
return (lans+rans)%mod;
}
}
void update_range(int x,int y,int z)
{
while(top[x]!=top[y]) // 两个点还不在一个条链上
{
if(dep[top[x]]<dep[top[y]]) // 深度大的点先向上跳跃
{
swap(x,y);
}
update(1,1,n,ID[top[x]],ID[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) // 在一条链上之后 深度小的结点的dfs序小
swap(x,y);
update(1,1,n,ID[x],ID[y],z);
}
int query_range(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
ans=(ans+query(1,1,n,ID[top[x]],ID[x]))%mod;
x=fa[top[x]];
}
if(dep[x]>dep[y])
{
swap(x,y);
}
ans=(ans%mod+query(1,1,n,ID[x],ID[y])%mod)%mod;
return ans;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt","r",stdin);
#endif
int mode,a,b,x,y,z;
memset(head,-1,sizeof head);
scanf("%d %d %d %d",&n,&m,&r,&mod);
for(int i=1;i<=n;i++)
scanf("%d",&temp_val[i]);
for(int i=1;i<n;i++)
{
scanf("%d %d",&a,&b);
add(a,b);
add(b,a);
}
// 第一次dfs得到每个点的深度和重儿子
// 以及每个点的父节点 和 以当前节点作为根的树的结点个数
// 第二次dfs给每个点重新标号,每个点的标号为他们的dfs序
dep[r]=1; // 根节点深度为 1
dfs1(r,-1); // 根节点无父节点
dfs2(r,r); // 从根节点开始第二次dfs
creat(1,1,n);
for(int i=0;i<m;i++)
{
scanf("%d",&mode);
if(mode==1)
{
scanf("%d %d %d",&x,&y,&z); // 将树上x到y上最短路径 上的所有点的值加上z
z=z%mod;
update_range(x,y,z);
}
if(mode==2)
{
scanf("%d %d",&x,&y); // 求树上x到y上最短路径上的所有点的值的和
printf("%d\n",query_range(x,y));
}
if(mode==3)
{
scanf("%d %d",&x,&z); // 以x作为根节点的子树上的所有结点值+z
z%=mod;
update(1,1,n,ID[x],ID[x]+size[x]-1,z);
}
if(mode==4)
{
scanf("%d",&x); //求以x作为根节点的子树的所有点权值和
printf("%d\n",query(1,1,n,ID[x],ID[x]+size[x]-1)%mod);
}
}
return 0;
}