长链剖分


长链剖分

顾名思义就是以深度最大的儿子为长儿子做的剖分。

主要思想在于使用指针,使得同一条长链上的 dp 值共用,以达到优化空间复杂度为 \(O(n)\) ,以及优化时间复杂度。但是前提是 dp 状态中有深度这一维。

上一个例题具体分析一下。

CF1009F Dominant Indices

题目大意:给定一棵以 \(1\) 为根的 \(n\) 个节点的树。现在对于每个点,求出一个最小的\(k\) 使得 \(d(u,k)\) 最大。其中 \(d(u,x)\) 表示以 \(u\) 为根的子树中,距离 \(u\)\(x\) 的节点数。

\(1\le n\le 10^6\)

题解

长链剖分板子题。设 \(f[u][i]\) 表示 \(d(u,v)\) 。容易得到转移式:

\[f[u][0]=1 \\ f[u][i]+=f[v][i-1](v\in son[u])\\ \]

发现 \(i\) 枚举的范围是要到深度层的,且dp状态中有深度,可以使用长链剖分。

剖分部分和重链剖分是一样的,不多赘述。我们暴力合并轻儿子的dp,然后考虑直接继承长儿子。这个我们用指针为每个长链分配内存,让 \(f[son[u]\ ]\) 指向 \(f[u]+1\) ,也就是 \(u\) 的dp数组上 \(d(u,1)\) 的位置,这样就可以达到 \(O(n)\) 的空间复杂度。

发现我们只是暴力合并了长链,长链的深度和为 \(n\) ,那么时间复杂度也是 \(O(n)\) 的。

参考代码:

#include
#define ll long long
#define db double
#define filein(a) freopen(#a".in","r",stdin)
#define fileot(a) freopen(#a".out","w",stdout)
#define sky fflush(stdout)
#define gc getchar
#define pc putchar
namespace IO{
	template
	inline void read(T &s){
		s=0;char ch=gc();bool f=0;
		while(ch<'0'||'9'
	inline void read(T &s,A &...a){
		read(s);read(a...);
	}
	inline bool blank(char c){
		return c==' ' or c=='\t' or c=='\n' or c=='\r' or c==EOF;
	}
	inline void gs(std::string &s){
		s+='#';char c=gc();
		while(blank(c) ) c=gc();
		while(!blank(c) ){
			s+=c;c=gc();
		}
	}
};
using IO::read;
using IO::gs;
const int N=1e6+3;
int n;
int head[N],nxt[N<<1];
struct Edge{
	int u,v;
}to[N<<1];
int Etot;
inline void link(int u,int v){
	nxt[++Etot]=head[u];
	head[u]=Etot;
	to[Etot]={u,v};
}
inline void _link1(int u,int v){
	link(u,v);link(v,u);
}
int dep[N],son[N];
int buf[N],*dp[N],*now;
void dfs1(int u,int f){
	for(int i=head[u];~i;i=nxt[i]){
		int v=to[i].v;
		if(v==f) continue;
		dfs1(v,u);
		if(dep[v]>dep[son[u] ]){
			son[u]=v;
		}
	}
	dep[u]=dep[son[u] ]+1;
}
int ans[N];
void dfs2(int u,int f){
	dp[u][0]=1;
	if(son[u]){
		dp[son[u] ]=dp[u]+1;
		//其儿子的答案指向其深度为1的答案
		dfs2(son[u],u);
		ans[u]=ans[son[u] ]+1;
	}
	for(int i=head[u];~i;i=nxt[i]){
		int v=to[i].v;
		if(v==f or v==son[u]) continue;
		dp[v]=now;now+=dep[v];
		dfs2(v,u);
		for(int i=1;i<=dep[v];++i){
			dp[u][i]+=dp[v][i-1];
			if(dp[u][i]>dp[u][ans[u] ]){
				ans[u]=i;
			}else if(dp[u][i]==dp[u][ans[u] ] and i

例题

[POI2014]HOT-Hotels