[模板] 长链剖分

长链剖分

简介

对每个节点 \(p\), 定义 \(\operatorname{len} (p)\) 表示 \(p\) 到它子树中叶子节点的最大长度.

类似重链剖分, 把每个节点 \(p\) 的儿子中 \(\operatorname{len}\) 最大的设为它的重儿子. 边 \((p,son(p))\) 称为重边.

把重边组成的极长子链称为重链(或者长链).

长链剖分可以保证一个性质:

\[\begin{equation} \text{长链总长度} \le n \end{equation} \]

虽然很显然, 但这个性质保证了一些利用长链剖分的算法的复杂度.

应用

求一个点的k级祖先

预处理 \(O(n \log n)\), 单次询问 \(O(1)\).

其实并没有什么卵用, 可以被倍增替代

优化一些关于长度的dp

先咕了...

例题: bzoj4543: [POI2014]Hotel加强版

可以得到一个关于链长度的dp方程, 因此利用长链剖分优化即可.

开空间时:

  • 对于\(f\) 数组, 重链顶 \(p\) 和它的重链上的儿子只会用到 \(mem[f[p] ... f[p]+len[p]-1]\) 的空间;
  • 对于\(g\) 数组, 重链顶 \(p\) 和它的重链上的儿子会用到 \(mem[g[p]-len[p]+1 ... g[p]+len[p]-1]\) 的空间.

因此开空间时需要开两倍... 详见代码.

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const int nsz=100050;

int n;
ll ans=0;
struct te{int t,pr;}edge[nsz*2];
int hd[nsz],pe=1;
void adde(int f,int t){edge[++pe]=(te){t,hd[f]};hd[f]=pe;}
void adddb(int f,int t){adde(f,t);adde(t,f);}
#define forg(p,i,v) for(int i=hd[p],v=edge[i].t;i;i=edge[i].pr,v=edge[i].t)

int len[nsz]{-1},son[nsz];
ll mem[nsz*5],*f[nsz],*g[nsz],*pm=mem;
void initp(int p,int l){ //开空间
	f[p]=pm,pm+=(l<<1)+2,g[p]=pm,pm+=l+2;
}

void dfs1(int p,int f){
	son[p]=0,len[p]=0;
	forg(p,i,v){
		if(v==f)continue;
		dfs1(v,p);
		if(len[v]>len[son[p]])son[p]=v,len[p]=len[v]+1;
	}
}

void dfs2(int p,int fa){
	if(son[p]){
		f[son[p]]=f[p]+1,g[son[p]]=g[p]-1;
		dfs2(son[p],p);
	}
	f[p][0]=1;
	ans+=g[p][0];
	forg(p,i,v){
		if(v==fa||v==son[p])continue;
		initp(v,len[v]);
		dfs2(v,p);
		rep(j,0,len[v]){
			if(j)ans+=f[p][j-1]*g[v][j];
			ans+=g[p][j+1]*f[v][j];
		}
		rep(j,0,len[v]){
			g[p][j+1]+=f[p][j+1]*f[v][j];
			if(j)g[p][j-1]+=g[v][j];
			f[p][j+1]+=f[v][j];
		}
	}
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	int a,b;
	rep(i,1,n-1)cin>>a>>b,adddb(a,b);
	dfs1(1,0);
	initp(1,len[1]);
	dfs2(1,0);
	cout<<ans<<'\n';
	return 0;
}
posted @ 2019-03-06 20:47  Ubospica  阅读(357)  评论(0编辑  收藏  举报