题目描述
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
输入输出格式
输入格式:
第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。
接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。
接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)
接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:
操作1: 1 x y z
操作2: 2 x y
操作3: 3 x z
操作4: 4 x
输出格式:
输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)
输入输出样例
输入样例#1:
5 5 2 24
7 3 7 8 0
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3
输出样例#1:
2
21
说明
时空限制:1s,128M
数据规模:
对于30%的数据: N≤10,M≤10
对于70%的数据: ,
对于100%的数据: ,
( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )
很简单的一个树剖板子了
支持四种操作:
1、链加
2、链求和
3、子树加
4、子树求和
其实可以dfs序可以做的
但这里为了练树剖我们还是用写树剖吧
链加
当两个点不在重链同一条重链上时,我们把顶端深度深的那条重链上的点跳到重链的父亲上,然后线段树维护区间加,直到两个点在同一条重链上后,再把两个点之间的区间加一下
链求和也是类似
当两个点不在同一条重链上时,把顶端深度深的点跳到重链的父亲,然后区间求和,最后两个点之间区间求和
子树加
直接在dfs序上维护区间加
子树求和
也直接在dfs序的区间上区间求和就是了
可以发现第1、2个操作因为要执行logn次,每次复杂度约为logn,所以复杂度为
而3、4个操作的复杂度是直接logn的
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)/2)
inline int read(){
char ch=getchar();
int res=0;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
int sum[400005],mark[400005],n,m,r,mod,adj[200005],nxt[200005],to[200004],cnt;
int fa[200005],son[200005],id[200005],dep[200005],siz[200005],top[200005],w[200005],val[200005];
inline void addedge(int u,int v){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
nxt[++cnt]=adj[v],adj[v]=cnt,to[cnt]=u;
}
int pushup(int rt)
{
sum[rt] = (sum[rt << 1] + sum[rt << 1 | 1]) % mod;
}
inline void pushdown(int u,int len)
{
if(mark[u])
{
mark[u*2]+=mark[u];
mark[(u*2)+1]+=mark[u];
sum[u*2]+=mark[u]*(len-(len>>1));
sum[u*2]%=mod;
sum[(u*2)+1]+=mark[u]*(len>>1);
sum[(u*2)+1]%=mod;
mark[u]=0;
}
}
inline void buildtree(int u,int l,int r,int dep)
{
if(l==r)
{
sum[u]=val[l];
sum[u]%=mod;
return ;
}
buildtree(u*2,l,mid,dep+1);
buildtree((u*2)+1,mid+1,r,dep+1);
pushup(u);
}
void update(int u,int l,int r,int L, int R, int c)
{
if (L <= l && r <= R)
{
mark[u] += c;
sum[u] += c * (r - l + 1);
sum[u] %= mod;
return;
}
pushdown(u, r - l + 1);
int m = (l + r) >> 1;
if (L <= m)
update(u*2,l,m,L, R, c);
if (R > m)
update((u*2)+1,m+1,r,L, R, c);
pushup(u);
}
inline int query(int u,int l,int r,int st,int des)
{
if(l>des||r<st) return 0;
int ans=0;
if(st<=l&&r<=des)
{
return sum[u];
}
pushdown(u,r-l+1);
if(st<=mid)
{
ans+=query(u*2,l,mid,st,des);
ans%=mod;
}
if(mid<des)
{
ans+=query((u*2)+1,mid+1,r,st,des);
ans%=mod;
}
return ans;
}
inline void dfs1(int u,int f)
{
dep[u]=dep[f]+1;
fa[u]=f,siz[u]=1;
int maxs=-1;
for(int e=adj[u];e;e=nxt[e])
{
int v=to[e];
if(v==f) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>maxs)
{
son[u]=v;
maxs=siz[v];
}
}
}
inline void dfs2(int u,int topfa)
{
id[u]=++cnt;
val[cnt]=w[u];
top[u]=topfa;
if(!son[u]) return;
dfs2(son[u],topfa);
for(int e=adj[u];e;e=nxt[e])
{
int v=to[e];
if(v==son[u]||v==fa[u]) continue;
dfs2(v,v);
}
}
inline void uprange(int x,int y,int k)
{
k%=mod;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],k);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,1,n,id[x],id[y],k);
}
inline int qrange(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=query(1,1,n,id[top[x]],id[x]);
ans%=mod;
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query(1,1,n,id[x],id[y]);
return ans%mod;
}
inline void upson(int x,int k){
update(1,1,n,id[x],id[x]+siz[x]-1,k);
}
inline int qson(int x)
{
return query(1,1,n,id[x],id[x]+siz[x]-1);
}
int main()
{
int u, v;
n=read(),m=read(),r=read(),mod=read();
for (int i = 1; i <= n; i++)
w[i]=read();
for (int i = 1; i <= n - 1; i++)
{
u=read(),v=read();
addedge(u, v);
}
cnt=0;
dfs1(r, 0);
dfs2(r, r);
buildtree(1, 1,n,1);
while (m--)
{
int op, x, y, z;
op=read();
if (op == 1)
{
x=read(),y=read(),z=read();
uprange(x, y, z);
}
else if (op == 2)
{
x=read(),y=read();
printf("%d\n", qrange(x, y));
}
else if (op == 3)
{
x=read(),z=read();
upson(x, z);
}
else if (op == 4)
{
x=read();
printf("%d\n", qson(x));
}
}
return 0;
}