#include<iostream>
#include<cstdio>
#define MAXN 1e7+5
using namespace std;
int hd[MAXN];
struct node{
int frm,to,nxt;
}edge[MAXN];
int cnt=0;
void add(int u,int v)
{
cnt++;
edge[cnt].to=v;
edge[cnt].frm=u;
edge[cnt].nxt=hd[u];
hd[u]=cnt;
}
void dfs1(int u,int f,int dep)
{
deep[u]=dep;
fa[u]=f;
siz[u]=1;
int maxson=-1;
for(int i=hd[u];i;i=edge[i].nxt )
{
int v=edge[i].to;
if(v==f)continue;
dfs1(v,u,dep+1);
siz[u]+=siz[v];
if(siz[v]>maxson)
maxson=siz[v],son[u]=v;
}
}
int cnt=0;
//把重儿子找出来,开始按顺序放到线段树排列中,
//还要记录top,重链上的top
dfs2(int u,int topf)
{
idx[u]=++cnt;
a[cnt]=b[u];//点权
top[u]=topf;
if(!son[u])return;
dfs2(son[u],topf);
for(int i=hd[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa[u] || v==son[u])continue;/
dfs2(v,v);
}
}
void pushup(int rt)
{
tre[rt].w=(tre[rt<<1].w+tre[rt<<1|1].w+MOD)%MOD;
}
void build(int rt,int L,int R)
{
tre[rt].l=L;
tre[rt].r=R;
tre[rt].siz=R-L+1;
if(L==R)
{
tre[rt].w=a[L];
return;
}
int mid=(L+R)>>1;
build(rt<<1,L,mid);
build(rt<<1|1,mid+1,R);
pushup(rt);
}
void pushdown(int rt)
{
if(tre[rt].f)
{
tre[rt<<1].w+=(tre[rt<<1].w+(tre[rt<<1].siz*tre[rt].f)%MOD)%MOD;
tre[rt<<1|1].w+=(tre[rt<<1|1].w+(tre[rt<<1|1].siz*tre[rt].f)%MOD)%MOD;
tre[rt<<1].f=(tre[rt<<1].f+tre[rt].f)%MOD;
tre[rt<<1|1].f=(tre[rt<<1|1].f+tre[rt<<1|1].f)%MOD;
tre[rt<<1].f=0;
}
}
void SegmentAdd(int rt,int L,int R,int val)
{
if(L<=tre[rt].l && R>=tre[rt].r)
{
tre[rt].w+=tre[rt].siz*val;
tre[rt].f+val;
return;
}
pushdown(rt);
int mid=(tre[rt].l+tre[rt].r)>>1;
if(L<=mid) SegmentAdd(rt<<1,L,R,val);
if(R>mid) SegmentAdd(rt<<1|1,L,R,val);
}
void TreeAdd(int x,int y,int val)//两点之间add一个数
{
while(top[x]!=top[y])//让x深
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
SegmentAdd(1,id[top[x]],idx[x],val);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
SegmentAdd(1,idx[x],idx[y],val);//让x浅
}
int SegmentSum(int rt,int L,int R)
{
int ans=0;
if(L<=tre[rt].l && R>=tre[rt].R)
return tre[rt].w;
pushdown(rt);
int mid=(tre[rt].l+tre[rt].r)>>1;
if(L<=mid)
ans=(ans+SegmentSum(rt<<1,L,R))%MOD;
if(R>mid)
ans=(ans+SegmentSum(rt<<1|1,L,R))%MOD;
return ans;
}
void TreeSum(int x,int y)//求两点之间的和
{
int ans=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) swap(x,y);//将x变深
ans=(ans+SegmentSum(1,idx[top[x]],idx[x])%MOD)%MOD;
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans=(ans+Segment(1,idx[x],idx[y])%MOD)%MOD;
printf("%d\n",ans);
}
int main()
{
scanf("%d%d%d%d",&n,&m,&rt,&MOD);
for(int i=1;i<=N;i++)
scanf("%d",&b[i]);
int x,y,z;
for(int i=1;i<=N;i++)
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(rt,0,1);
dfs2(rt,rt);
build(1,1,n);
while(m--)
{
int opt,x,y,z;
if(opt==1)
{
scanf("%d%d%d",&x,&y,&z);z%=MOD;
TreeAdd(x,y,z);
}
else if(opt==2)
{
scanf("%d%d",&x,&y);
TreeSum(x,y);//x到y的和
}
else if(opt==3)
{
scanf("%d%d",&x,&y);
SegmentAdd(1,idx[x],idx[x]+size[x]-1,z%MOD);
}
else if(opt==4)
{
scanf("%d",&x);
printf("%d\n",SegmentSum(1,idx[x],idx[i]+size[x]-1)%MOD) ;
}
}
return 0;
}
树剖
最新推荐文章于 2023-07-07 07:18:42 发布