树链剖分总结
树链剖分总结
原理
将树边归到至多\(logn\)条连续树边构成的重边中,再在各条重边上建线段树,每次操作先遍历所有经过的重边再依次线段树上操作,复杂度\({logn}^2\)
树剖代码量并不大,只是其包含了线段树;树剖本身并不难,只是看你怎么维护线段树
实现
初始化
两次\(dfs\)求得所需信息
第一次求得每个节点重儿子mxs[]
、深度等
int fa[MAXN],dep[MAXN],sz[MAXN],mxs[MAXN];
void dfs1(int u, int f){
fa[u]=f;
dep[u]=dep[f]+1;
sz[u]=1;
int mxsz=-1;
for(int i=head[u];i;i=nxt[i]){
int v=vv[i];
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>mxsz){
mxsz=sz[v];
mxs[u]=v;
}
}
}
第二次划分重边topf[]
、\(dfs\)序idx[]
int idx[MAXN],topf[MAXN],cnt;
void dfs2(int u, int top){
idx[u]=++cnt;
topf[u]=top;
if(mxs[u]==0) return;
dfs2(mxs[u], top);
for(int i=head[u];i;i=nxt[i]){
int v=vv[i];
if(v==fa[u]||v==mxs[u]) continue;
dfs2(v,v);
}
}
查询&修改
其实都差不多一样,比如下面查询链上最大值的
int tre_qury(int a, int b){
int ans=-INF;
while(topf[a]!=topf[b]){
if(dep[topf[a]]<dep[topf[b]]) swap(a,b);
ans=max(ans, query(1, idx[topf[a]], idx[a]));
a=fa[topf[a]];
}
if(dep[a]<dep[b]) swap(a,b);
ans=max(ans, query(1, idx[b], idx[a]));
return ans;
}
如果查询或修改的是子树,那么利用\(dfs\)序性质即可:
inline int son_query(int x){
return query(1, idx[x], idx[x]+sz[x]-1);
}