惨了惨了我好喜欢这个算法呜呜呜快超过(MST)了
树剖涉及的知识:(LCA)(最近公共祖先/倍增),线段树
已知一棵包含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为根节点的子树内所有节点值之和
(我理解中的)树剖就是将树上的问题,转化为线性来处理。
通过两次搜索,对每个结点加以一些标记,其中还包括对结点进行新的编号
编号之后就满足一个结点为根的子树,它们的新编号是连续的。
于是就可以维护区间和了w!(线段树好啊233)
(懒得写太多了)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN 233333
//变量们!
int n,m,r,mod;
int tot=0,cnt=0;
int ans[MAXN<<2],tag[MAXN<<2];
struct qwq
{
int nex,to;
}e[MAXN];
int h[MAXN];
int w1[MAXN],w2[MAXN];
int dep[MAXN],top[MAXN],siz[MAXN],fa[MAXN],id[MAXN],son[MAXN];
//end.
void add(int x,int y)
{
e[++tot].to=y;
e[tot].nex=h[x];
h[x]=tot;
}
//seg_tree
#define leftson cur<<1
#define rightson cur<<1|1
#define mid ((l+r)>>1)
#define push_up ans[cur]=ans[leftson]+ans[rightson]; ans[cur]%=mod
#define push_down lazyadd(leftson,l,mid,tag[cur]); lazyadd(rightson,mid+1,r,tag[cur]); tag[cur]=0
inline void lazyadd(int cur,int l,int r,int del)
{
tag[cur]+=del;
ans[cur]+=del*(r-l+1);
ans[cur]%=mod;
}
inline void build(int cur,int l,int r)
{
if (l==r)
{
ans[cur]=w2[l]%mod;
return;
}
build(leftson,l,mid);
build(rightson,mid+1,r);
push_up;
}
inline void change(int adl,int adr,int cur,int l,int r,int del)
{
if(adl<=l&&r<=adr)
{
ans[cur]+=del*(r-l+1)%mod;
tag[cur]+=del;
return;
}
push_down;
if (adl<=mid) change(adl,adr,leftson,l,mid,del);
if (adr>mid) change(adl,adr,rightson,mid+1,r,del);
push_up;
}
#define ll long long
ll query(int ql,int qr,int cur,int l,int r)
{
if (ql<=l&&r<=qr)
{
return ans[cur];
}
push_down;
ll answer=0;
if (ql<=mid) answer+=query(ql,qr,leftson,l,mid)%mod;
if (qr>mid) answer+=query(ql,qr,rightson,mid+1,r)%mod;
return answer%mod;
}
//end.
inline void dfs_fir(int x,int f,int dept)
{
dep[x]=dept;
fa[x]=f;
siz[x]=1;
int maxn=-1;
for (int i=h[x],y;i;i=e[i].nex)
{
y=e[i].to;
if (y==f) continue;
dfs_fir(y,x,dept+1);
siz[x]+=siz[y];
if (siz[y]>maxn)
{
son[x]=y;
maxn=siz[y];
}
}
}
inline void dfs_sec(int x,int ft)
{
id[x]=++cnt;
// printf("id::%d",cnt);
w2[cnt]=w1[x];
top[x]=ft;
if (!son[x]) return;
dfs_sec(son[x],ft);
int y;
for (int i=h[x];i;i=e[i].nex)
{
y=e[i].to;
if (y==fa[x]||y==son[x]) continue;
dfs_sec(y,y);
}
}
inline ll query_ans(int x,int y)
{
ll answ=0;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
answ+=query(id[top[x]],id[x],1,1,n)%mod;
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
answ+=query(id[x],id[y],1,1,n);
return answ%mod;
}
inline void upd_tree(int x,int y,int del)
{
del%=mod;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
change(id[top[x]],id[x],1,1,n,del);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
change(id[x],id[y],1,1,n,del);
}
inline ll query_sontree(int x)
{
return query(id[x],id[x]+siz[x]-1,1,1,n)%mod;
}
inline void upd_sontree(int x,int del)
{
change(id[x],id[x]+siz[x]-1,1,1,n,del);
}
//////
int main()
{
scanf("%d%d%d%d",&n,&m,&r,&mod);
for (int i=1;i<=n;i++)
{
scanf("%d",&w1[i]);
}
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs_fir(r,0,1);
dfs_sec(r,r);
build(1,1,n);
int q,x,y,z;
while (m--)
{
scanf("%d",&q);
if (q==1)
{
scanf("%d%d%d",&x,&y,&z);
upd_tree(x,y,z);
continue;
}
if (q==2)
{
scanf("%d%d",&x,&y);
printf("%lld
",query_ans(x,y));
continue;
}
if (q==3)
{
scanf("%d%d",&x,&y);
upd_sontree(x,y);
continue;
}
scanf("%d",&x);
printf("%lld
",query_sontree(x));
}
return 0;
}
(因为是模板所以把涉及到的知识点的标签都加上了w)