惨了惨了我好喜欢这个算法呜呜呜快超过\(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\n",query_ans(x,y)); continue; } if (q==3) { scanf("%d%d",&x,&y); upd_sontree(x,y); continue; } scanf("%d",&x); printf("%lld\n",query_sontree(x)); } return 0; }
(因为是模板所以把涉及到的知识点的标签都加上了w)
来源:https://www.cnblogs.com/Kan-kiz/p/10933611.html