树链剖分总结

树链剖分总结

原理

将树边归到至多(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);
}
原文地址:https://www.cnblogs.com/santiego/p/11238602.html