D12【模板】重链剖分/树链剖分
D12【模板】重链剖分/树链剖分_哔哩哔哩_bilibili
给一颗树,4种操作:路径加,路径和,子树加,子树和。
重儿子 表示其子结点中子树最大的子结点.
轻儿子 表示剩余的所有子结点.
连向重儿子的边为 重边.
若干条首尾衔接的重边构成 重链.每条重链的头一定是 轻子结点.那么整棵树就被剖分成若干条重链.
树上每个结点都属于且仅属于一条重链.

1. 第一个 DFS 记录每个结点的父结点(𝑓𝑎
)、深度(𝑑𝑒𝑝
)、子树大小(𝑠𝑖𝑧
)、重儿子(𝑠𝑜𝑛
).
2. 第二个 DFS 记录所在链的链顶(𝑡𝑜𝑝
)、重边优先遍历时的 DFS 序(dfn
)、DFS 序对应的结点编号(𝑟𝑛𝑘
).
- dfn(𝑥)
表示结点 𝑥
的 DFS 序,也是其在线段树中的编号. - rnk(𝑥)
表示 DFS 序所对应的结点编号,有 rnk(dfn(𝑥)) =𝑥
.
3. 把树拆成链,用链建立线段树.
4. 路径拆成链,线段树上做区修、区查.
// 树链剖分 O(mlognlogn) #include<bits/stdc++.h> using namespace std; #define int long long const int N=100010; int n,m,R,P,w[N]; vector<int> e[N]; // 树链剖分 int fa[N],dep[N],siz[N],son[N]; int top[N],dfn[N],rnk[N]; void dfs1(int u,int f){ //搞fa,dep,siz,son fa[u]=f,dep[u]=dep[f]+1,siz[u]=1; for(int v:e[u])if(v!=f){ dfs1(v,u); siz[u]+=siz[v]; if(siz[son[u]]<siz[v]) son[u]=v; } } void dfs2(int u,int t){ //搞top,dfn,rnk top[u]=t,dfn[u]=++dfn[0],rnk[dfn[0]]=u; if(son[u]) dfs2(son[u],t); for(int v:e[u])if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } struct SGT{ //线段树 #define lc (u<<1) #define rc (u<<1|1) int sum[N<<2],add[N<<2]; //节点和,节点加 void pushup(int u){ sum[u]=sum[lc]+sum[rc]; } void pushdown(int u,int l,int r,int mid){ if(add[u]){ sum[lc]+=add[u]*(mid-l+1); sum[rc]+=add[u]*(r-mid); add[lc]+=add[u]; add[rc]+=add[u]; add[u]=0; } } void build(int u=1,int l=1,int r=n){ //建线段树 sum[u]=w[rnk[l]]; if(l==r) return; int mid=l+r>>1; build(lc,l,mid); build(rc,mid+1,r); pushup(u); } void upd(int x,int y,int k,int u=1,int l=1,int r=n){ //线段树修改 if(x>r||y<l) return; if(x<=l&&r<=y){ sum[u]+=k*(r-l+1); add[u]+=k; return; } int mid=l+r>>1; pushdown(u,l,r,mid); upd(x,y,k,lc,l,mid); upd(x,y,k,rc,mid+1,r); pushup(u); } void upd_path(int u,int v,int k){ //修改路径 while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); upd(dfn[top[u]],dfn[u],k); u=fa[top[u]]; } if(dep[u]<dep[v]) swap(u,v); upd(dfn[v],dfn[u],k); //最后一段 } void upd_tree(int u,int k){ //修改子树 upd(dfn[u],dfn[u]+siz[u]-1,k); } int ask(int x,int y,int u=1,int l=1,int r=n){ //线段树查询 if(x>r||y<l) return 0; if(x<=l&&r<=y) return sum[u]; int mid=l+r>>1; pushdown(u,l,r,mid); return ask(x,y,lc,l,mid)+ask(x,y,rc,mid+1,r); } int ask_path(int u,int v){ //查询路径 int res=0; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); res+=ask(dfn[top[u]],dfn[u]); u=fa[top[u]]; } if(dep[u]<dep[v]) swap(u,v); res+=ask(dfn[v],dfn[u]); //最后一段 return res; } int ask_tree(int u){ //查询子树 return ask(dfn[u],dfn[u]+siz[u]-1); } }S; signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m>>R>>P; for(int i=1; i<=n; i++) cin>>w[i]; for(int i=0,a,b; i<n-1; i++){ cin>>a>>b; e[a].push_back(b); e[b].push_back(a); } dfs1(R,0); dfs2(R,R); //树剖 S.build(); //建线段树 for(int t,a,b,c;m--;){ cin>>t>>a; if(t==1) cin>>b>>c,S.upd_path(a,b,c); //路径加 else if(t==3) cin>>c,S.upd_tree(a,c); //子树加 else if(t==2) cin>>b,cout<<S.ask_path(a,b)%P<<"\n"; //路径和 else cout<<S.ask_tree(a)%P<<"\n"; //子树和 } }
Luogu P5305 [GXOI/GZOI2019] 旧词
浙公网安备 33010602011771号