標籤:back ret 表示 while 變數 str div class lock
基本思想
樹鏈剖分一種對樹進行劃分的演算法,它先通過輕重邊剖分將樹分為多條鏈,保證每條邊屬於且只屬於一條鏈,然後再通過資料結構來維護每一條鏈。
一些定義
樹鏈:樹上的路徑.
剖分:把路徑分類為重鏈和輕鏈.
重兒子:u的子節點中siz[v]值最大的v.
輕兒子:u的其它子節點.
重邊:點u與其重兒子的連邊.
輕邊:點u與其輕兒子的連邊.
重鏈:由重邊連成的路徑.
輕鏈:輕邊.
性質
- 如果(u,v)為輕邊,則siz[v]$\times$2<siz[u].
- 從根到某一點的路徑上輕鏈、重鏈的個數都不大於log$\;$n.
- 樹剖序其實也可以是dfs序的一種.
實現
一些變數:
f[u]表示u的父親.
siz[u]表示以u為根的子樹的大小.
dep[u]表示u的深度(根深度為1).
top[u]表示u所在的鏈的頂端節點.
son[u]表示與u的重兒子.
重標號:
p[u]:重標號後u的編號.
dfs序:dfs的時候先走重邊.
這樣可以使得重邊的編號是連續的,方便維護.
int f[N],p[N],dep[N],siz[N],son[N],top[N];/*top[u]:u所在的鏈的頂端節點,son[u]:u的重兒子*/inline void dfs1(int u){ int m=0;siz[u]=1; for(int i=g[u];i;i=e[i].nxt) if(!dep[e[i].to]){ f[e[i].to]=u; dep[e[i].to]=dep[u]+1; dfs1(e[i].to); siz[u]+=siz[e[i].to]; if(siz[e[i].to]>m){ son[u]=e[i].to; m=siz[e[i].to]; } }}inline void dfs2(int u,int tp){ top[u]=tp;p[u]=++cnt;ww[cnt]=w[u]; if(son[u]) dfs2(son[u],tp); for(int i=g[u];i;i=e[i].nxt){ if(e[i].to!=f[u]&&e[i].to!=son[u]) dfs2(e[i].to,e[i].to); }}
類似倍增的走法,每次將深度大的往上移,直到u,v屬於同一條鏈.
inline int sum(int x,int y){ int ret=0,t; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]){ t=x;x=y;y=t; } ret+=ask(1,p[top[x]],p[x]); x=f[top[x]]; } if(p[x]>p[y]){ t=x;x=y;y=t; } ret+=ask(1,p[x],p[y]); return ret; }
inline void change(int x,int y,int k){ int t; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]){ t=x;x=y;y=t; } cover(1,p[top[x]],p[x],k); x=f[top[x]]; } if(p[x]>p[y]){ t=x;x=y;y=t; } cover(1,p[x],p[y],k);}
[學習筆記]樹鏈剖分