树链剖分
思想:把树拆分成 一条一条的线段(利用重链) 在变成1条线(从而利用线段树处理)
应用: 主要是 对数的lca求解,并且对lca的路径上进行查询和修改。
两次dfs 进行构建
第一次 为拆分树做准备: 重儿子 son,每一个点的深度,父亲,尺寸;
int dep[M],vis[M],fa[M],son[M],sz[M]; void dfs1(int a,int f) { vis[a]=1; dep[a]=dep[f]+1; fa[a]=f; sz[a]++; int mx=0; for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(vis[b]) continue; dfs1(b,a); sz[a]+=sz[b]; if(sz[b]>mx) { mx=sz[b]; son[a]=b; } } }
第二次 进行拆树 给每一个节点建立一个新id (用于线段树)
void dfs2(int a,int f) { vis[a]=1; top[a]=f; id[a]=++cur; addr[cur]=a; if(son[a]) dfs2(son[a],f); for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; if(vis[b]) continue; dfs2(b,b); } hao[a]=cur; }
LCA:
void x_lca(int a,int b) { while(top[a]!=top[b]) { if(dep[top[a]]<dep[top[b]]) swap(a,b); xiu(id[top[a]],id[a],1,x); a=fa[top[a]]; } if(dep[a]<dep[b]) swap(a,b); xiu(id[b],id[a],1,x); } long long q_lca(int a,int b) { long long ans=0; while(top[a]!=top[b]) { if(dep[top[a]]<dep[top[b]]) swap(a,b); ans+=qu(id[top[a]],id[a],1); a=fa[top[a]]; } if(dep[a]<dep[b]) swap(a,b); ans+=qu(id[b],id[a],1); return ans; }
线段树:
struct dian{ int l,r,val; }p[M*4]; int lz[M*4]; int val[M]; void up(int i) { p[i].val=p[i<<1].val+p[i<<1|1].val; } void build(int l,int r,int i) { p[i].l=l,p[i].r=r; if(l==r) { p[i].val=val[addr[l]]; return ; } int mid=(l+r)>>1; build(l,mid,i<<1); build(mid+1,r,i<<1|1); up(i); } void down(int i) { lz[i<<1]+=lz[i]; lz[i<<1|1]+=lz[i]; p[i<<1].val+=(p[i<<1].r-p[i<<1].l+1)*lz[i]; p[i<<1|1].val+=(p[i<<1|1].r-p[i<<1|1].l+1)*lz[i]; lz[i]=0; } void xiu(int l,int r,int i,int x) { if(p[i].l>r||p[i].r<l) return ; if(l<=p[i].l&&p[i].r<=r) { lz[i]+=x; p[i].val+=(p[i].r-p[i].l+1)*x; return ; } if(lz[i]) down(i); xiu(l,r,i<<1,x); xiu(l,r,i<<1|1,x); up(i); } long long qu(int l,int r,int i) { if(p[i].l>r||p[i].r<l) return 0; if(l<=p[i].l&&p[i].r<=r) { return p[i].val; } if(lz[i]) down(i); return qu(l,r,i<<1)+qu(l,r,i<<1|1); }
后记:
- dfs时 vis记得写,并且在2次bfs中间 记得对vis清零
- sz本身节点要+1,儿子的尺寸也要加上去
- 重链的更新 (b,f) (b,b);
- 先定义变量,在写dfs,写dfs 反过去看看是否变量都处理了没有
- LCA,用线段树的时候 都是写 ID,不是本身的!!!
- 是dep[top[a]]的深度,不是dep【a】;当然后面就是那个
- 深度小在前面
- build时,是 val【addr【l】】,不是直接L,这个l,r是新的id
- 其他的注意都是线段树的了。 上下+return