长链剖分优化 dp 学习笔记

定义:

  • 重儿子:子树内深度最大值最大的儿子。
  • 重边:连向重儿子的边。
  • 重链:若干条相邻重边连接的及其端点的一条链,并且是极长的。

长剖优化 dp 一般都是 dp 式子有一维和深度或距离相关的,于是可以在处理时优先递归重儿子并继承其 dp 值,然后遍历其轻儿子。

因为每条重链都只在其链顶的父亲处被遍历到,而所有重链的长度之和为 \(n\),所以复杂度为 \(O(n)\)

用长剖是因为我们有时会把信息保存在其 dfs 序上,如果保存轻儿子则可能出现重儿子的 dp 序列比这个儿子要长就合并不了。

P4292 [WC2010] 重建计划

长剖优化 dp。

看见分式先套一层二分,求答案是否能 \(\ge mid\)。路径长度都减去 \(mid\)。现在变成求是否存在一条长度在 \([L,U]\) 之间的路径其路径长度 \(\ge 0\)

\(f_{u,x}\) 为点 \(u\) 向下走 \(x\) 条边的最长距离。

重儿子暴力合并,轻儿子合并的时候记录一下答案即可。

记录答案考虑线段树维护 \(f\),可以维护到其 dfs 序然后用线段树上这样不用每次清空,然后每次合并一个轻儿子 \(f_{v,x}\) 就查一下区间最值更新答案即可。

#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define dbg(x) sd cout<<#x<<":"<<x<<" "
#define dg(x) sd cout<<#x<<":"<<x<<"\n"
#define inf 1e13
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=5e5+10,P=1e9+7;
const double eps=1e-5;
int n,dfn[N],num,son[N],dep[N],W[N],up,down;//上下界
sd vector<pii> G[N];
double s[N<<2],lazy[N<<2];//区间维护最大值
#define ls k<<1
#define rs k<<1|1
void clear(int k,int l,int r)
{
	s[k]=lazy[k]=0;
	if(l==r) return;
	int mid=l+r>>1;
	clear(ls,l,mid);
	clear(rs,mid+1,r);
}
void pushdown(int k)
{
	if(!lazy[k]) return;
	s[ls]+=lazy[k];
	s[rs]+=lazy[k];
	lazy[ls]+=lazy[k];
	lazy[rs]+=lazy[k];
	lazy[k]=0;
}
void update(int k,int l,int r,int x,int y,double v)//区间加
{
	if(x<=l&&y>=r)
	{
		s[k]+=v;
		lazy[k]+=v;
		return;
	}
	pushdown(k);
	int mid=l+r>>1;
	if(x<=mid) update(ls,l,mid,x,y,v);
	if(y>mid) update(rs,mid+1,r,x,y,v);
	s[k]=sd max(s[ls],s[rs]);
}
double ask(int k,int l,int r,int x,int y)
{
	if(x<=l&&y>=r) return s[k];
	pushdown(k);
	int mid=l+r>>1;
	double res=-inf;
	if(x<=mid) res=sd max(res,ask(ls,l,mid,x,y));
	if(y>mid) res=sd max(res,ask(rs,mid+1,r,x,y));
	return res;
}
void dfs1(int u,int fa)//dep[u]为u最多能向下走几条边
{
	dep[u]=0;
	for(auto [v,w]:G[u])
	{
		if(v==fa) continue;
		dfs1(v,u);
		if(!son[u]||dep[v]>dep[son[u]]) son[u]=v,W[u]=w;
		dep[u]=sd max(dep[v]+1,dep[u]);
	}
}
void dfs2(int u,int fa)
{
	dfn[u]=++num;
	if(son[u]) dfs2(son[u],u);
	for(auto [v,w]:G[u]) if(v!=fa&&v!=son[u]) dfs2(v,u);
}
double mid,val;
void dp(int u,int fa)
{
	if(son[u])
	{
		dp(son[u],u);
		// dg(u);
		// dbg(dfn[u]+1),dbg(dfn[u]+dep[u]),sd cout<<1.0*W[u]-mid<<"\n";
		update(1,1,n,dfn[u]+1,dfn[u]+dep[u],1.0*W[u]-mid);
		int L=dfn[u]+down,R=dfn[u]+sd min(dep[u],up);
		// dbg(L),dg(R);
		if(L<=R)
		{
			val=sd max(val,ask(1,1,n,L,R));
			// dg(u);
			// sd cout<<val<<"\n";
		}
	}
	for(auto [v,w]:G[u])
	{
		if(v==fa||v==son[u]) continue;
		dp(v,u);
		F(i,dfn[v],dfn[v]+dep[v])//先计算答案
		{
			int cnt=i-dfn[v]+1;//实际边数
			// dbg(i);
			// dg(cnt);
			int L=sd max(0ll,down-cnt),R=sd min(dep[u],up-cnt);
			// dbg(L),dg(R);
			if(L>R) continue;
			L+=dfn[u],R+=dfn[u];
			val=sd max(val,ask(1,1,n,i,i)+ask(1,1,n,L,R)+w-mid);
		}
		F(i,dfn[v],dfn[v]+dep[v])//合并
		{
			int cnt=i-dfn[v]+1;
			double c=(ask(1,1,n,i,i)+w-mid)-ask(1,1,n,dfn[u]+cnt,dfn[u]+cnt);
			if(c>eps) update(1,1,n,dfn[u]+cnt,dfn[u]+cnt,c);
		}
	}
}
int check()
{
	val=-inf;
	clear(1,1,n);
	dp(1,0);
	// puts("val:");
	// sd cout<<val<<"\n";
	return val>=0;
}
void solve()
{
	n=read();
	down=read(),up=read();
	F(i,2,n)
	{
		int x=read(),y=read(),z=read();
		G[x].emplace_back(y,z);
		G[y].emplace_back(x,z);
	}
	dfs1(1,0);
	dfs2(1,0);
	
	// F(i,1,n) printk(dep[i]),put(dfn[i]);
	// puts("");
	// put(son[1]);
	
	// mid=5;
	// put(check());
	
	double l=0,r=1e6,ans=0;
	while(r-l>eps)
	{
		clear(1,1,n);
		mid=(l+r)/2;
		if(check()) ans=mid,l=mid;
		else r=mid;
	}
	printf("%.3lf",ans);
}
signed main()
{
// 	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int T=1;
	// T=read();
	while(T--) solve();
    return 0;
}
posted @ 2025-10-24 21:47  _E_M_T  阅读(13)  评论(0)    收藏  举报