【Codeforces】CF932F Escape Through Leaf

题目描述

CF方向
Luogu方向

题目解法

这道题的 d p dp dp 式子是很容易列的,即 d p u = m i n { a u ∗ b v + d p v } dp_u=min\{a_u*b_v+dp_v\} dpu=min{aubv+dpv},其中 v v v u u u 的子树中
可以发现转移式子是一个一次函数的形式,可以想到用李超线段树维护

考虑一个较为暴力的做法:
对于每个 u u u,暴力加入每一个 v v v,即 b v b_v bv 为斜率, d p v dp_v dpv 为截距, a u a_u au 为自变量,每条一次函数的范围是无限的
这是可以用李超线段树维护的
这里需要注意 a u a_u au 可能为负数,所以需要把 a a a 加上一个偏移量,使 a a a 数组为正数,在计算函数值时在减回来

发现上述做法可以用线段树合并来替代
即先合并儿子,在计算答案,在加入当前点
时间复杂度 O ( n    l o g n ) O(n\;logn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(200100),M(200100),Del(100000);
int n,tans,a[N],b[N],dp[N]; 
int e[M],ne[M],h[N],idx;
int root[N],seg[N*20],lc[N*20],rc[N*20];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void add(int x,int y){
	e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}
int comp(int id1,int id2,int p){
	if(!id1||!id2) return id1|id2;
	int v1=b[id1]*(p-Del)+dp[id1];
	int v2=b[id2]*(p-Del)+dp[id2];
	return v1<v2?id1:id2;
}
void modify(int p,int l,int r,int id){
	if(!seg[p]){ seg[p]=id;return;}
	if(l==r){ seg[p]=comp(seg[p],id,l);return;}
	int mid=(l+r)>>1;
	if(comp(seg[p],id,mid)==id) swap(seg[p],id);
	int lp=comp(seg[p],id,l),rp=comp(seg[p],id,r);
	if(rp==id){
		if(!rc[p]) rc[p]=++idx;
		modify(rc[p],mid+1,r,id);
	}
	if(lp==id){
		if(!lc[p]) lc[p]=++idx;
		modify(lc[p],l,mid,id);
	}
}
int merge(int p,int q,int l,int r){
	if(!p||!q) return p|q;
	int mid=(l+r)>>1;
	if(l==r) return p;
	lc[p]=merge(lc[p],lc[q],l,mid);
	rc[p]=merge(rc[p],rc[q],mid+1,r);
	modify(p,l,r,seg[q]);
	return p; 
}
void query(int p,int l,int r,int pos){
	tans=comp(tans,seg[p],pos);
	if(l==r) return;
	int mid=(l+r)>>1;
	if(mid>=pos&&lc[p]) query(lc[p],l,mid,pos);
	if(mid<pos&&rc[p]) query(rc[p],mid+1,r,pos);
}
void dfs(int u,int fa){
	root[u]=++idx;
	bool isleaf=0;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(v==fa) continue;
		isleaf=1,dfs(v,u);
		root[u]=merge(root[u],root[v],1,N-1);
	}
	if(isleaf){
		tans=seg[root[u]];
		query(root[u],1,N-1,a[u]);
		dp[u]=b[tans]*(a[u]-Del)+dp[tans];
	}
	modify(root[u],1,N-1,u);
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),a[i]+=Del;
	for(int i=1;i<=n;i++) b[i]=read();
	memset(h,-1,sizeof(h));
	for(int i=1,x,y;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
	dfs(1,-1);
	for(int i=1;i<=n;i++) printf("%lld ",dp[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值