Loading

BZOJ1767 [CEOI2009] Harbingers - 树上斜率优化

题解

\(f_i\) 为从点 \(i\) 走到点 \(1\) 的最小时间,那么有 \(f_i=w_i+\min\limits_{j\in anc_i}\{v_i\operatorname{dist}(i,j)+f_j\}\)

\(\operatorname{dist}\) 拆开,就能写成一般的斜率优化形式。但如果对于每个点,都弹出单调栈末尾的不单调的决策,这个点的子树遍历完再把这些弹出的点放回单调栈里,那时间复杂度是错的。

考虑到对于每个点,实际上只需要更改单调栈内一个位置的值:二分出这个点在单调栈内应该插入的位置,并更改这个位置,然后更新单调栈长度即可。

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int N=1e5+5;
int n,head[N],cntt=1;
struct Edge{int v,nxt;ll w;}e[N<<1];
void AddEdge(int u,int v,ll w){
	e[++cntt]=Edge{v,head[u],w};head[u]=cntt;
}
ll f[N],w[N],v[N],dep[N];
bool Pop(int i,int j,int k){
	return __int128((f[i]-f[j]))*(dep[j]-dep[k])>__int128((dep[i]-dep[j]))*(f[j]-f[k]);
}
int stk[N],tail=0;
void Dfs(int u,int fa){
	if(tail>=1){
		int l=1,r=tail;
		while(l<r){
			int mid=(l+r)>>1;
			if(v[u]*(dep[stk[mid]]-dep[stk[mid+1]])>f[stk[mid]]-f[stk[mid+1]])
				r=mid;
			else
				l=mid+1;
		}
		f[u]=w[u]+v[u]*(dep[u]-dep[stk[l]])+f[stk[l]];
	}
	int pre=tail,orig,ins;
	if(tail>=1){
		int l=2,r=tail+1;
		while(l<r){
			int mid=(l+r)>>1;
			if(Pop(stk[mid-1],stk[mid],u)) r=mid;
			else l=mid+1;
		}
		ins=tail=l;
	}else ins=tail=1;
	orig=stk[tail];stk[tail]=u;
	for(int i=head[u];i;i=e[i].nxt){
		if(e[i].v==fa) continue;
		dep[e[i].v]=dep[u]+e[i].w;
		Dfs(e[i].v,u);
	}
	stk[ins]=orig,tail=pre;
}
int main(){
	Read(n);
	For(i,1,n-1){
		int u,v,w;Read(u,v,w);
		AddEdge(u,v,w);AddEdge(v,u,w);
	}
	For(i,2,n) Read(w[i],v[i]);
	Dfs(1,0);
	For(i,2,n) printf("%lld ",f[i]);
	return 0;
}
posted @ 2021-07-31 13:36  Alan_Zhao_2007  阅读(112)  评论(0编辑  收藏  举报