cf932F Escape Through Leaf dp+李超树

90 篇文章 0 订阅
40 篇文章 0 订阅

Description


有一棵以 1 号点为根的树,有 n−1 条边 ui,vi,每个点两个权值 Ai,Bi。 你可以从一个点 u 跳到另一个点 v 满足 v 在 u 的子树中,并付出 Au ·Bv 的代价。 定义终止节点为没有任何儿子的节点。对于每个节点,求出从这个点出发到达任意一 个终止节点的最小代价。

Constraints


对于 30% 的数据,n ≤ 5∗103。
对于另外 10% 的数据,∀i Bi = 1。
对于另外 30% 的数据,树是一条链,且除 1 号点外,i 号点的父亲是 i−1。
对于 100% 的数据,2 ≤ n ≤ 105,|Ai|,|Bi|≤ 105。

Solution


考虑dp,设f[i]为第i个点的答案,转移比较显然
可以发现如果把b看成斜率,f看成截距,那么我们实际上在找某一条垂直于x轴的直线与子树内直线的最低交点,这个点显然只在凸壳上
这里写的是李超树合并,然鹅我并不会证复杂度。std给出的是set维护凸壳,启发式合并的做法。

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const LL INF=1000000000000000LL;
const int N=200005;

struct line {LL k,b;};
struct edge {int y,next;} e[N*2];
struct treeNode {int l,r; line rec;} t[N*51];

int a[N],b[N];
int root[N],tot;
int ls[N],edCnt;

LL f[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

double get_x(line a,line b) {
	return (double)(a.b-b.b)/(b.k-a.k);
}

LL get_y(line a,LL x) {
	return a.k*x+a.b;
}

void modify(int &now,int tl,int tr,line x) {
	if (!now) {
		t[now=++tot].rec=x;
		return ;
	}
	if (get_y(t[now].rec,tl)<get_y(x,tl)&&get_y(t[now].rec,tr)<get_y(x,tr)) return ;
	if (get_y(t[now].rec,tl)>get_y(x,tl)&&get_y(t[now].rec,tr)>get_y(x,tr)) {
		t[now].rec=x; return ;
	}
	double pos=get_x(t[now].rec,x);
	int mid=(tl+tr)>>1;
	if (t[now].rec.k<x.k) {
		if (pos>mid) {
			modify(t[now].r,mid+1,tr,t[now].rec);
			t[now].rec=x;
		} else modify(t[now].l,tl,mid,x);
	} else {
		if (pos<mid) {
			modify(t[now].l,tl,mid,t[now].rec);
			t[now].rec=x;
		} else modify(t[now].r,mid+1,tr,x);
	}
}

LL query(int now,int tl,int tr,int x) {
	if (!now) return INF;
	if (tl==tr) return get_y(t[now].rec,x);
	int mid=(tl+tr)>>1;
	LL tmp,ret=get_y(t[now].rec,x);
	if (x<=mid) tmp=query(t[now].l,tl,mid,x);
	else tmp=query(t[now].r,mid+1,tr,x);
	return std:: min(ret,tmp);
}

int merge(int x,int y,int tl,int tr) {
	if (!x||!y) return x+y;
	if (tl==tr) {
		if (get_y(t[y].rec,tl)<get_y(t[x].rec,tl)) {
			t[x].rec=t[y].rec;
		}
		return x;
	}
	int mid=(tl+tr)>>1;
	t[x].l=merge(t[x].l,t[y].l,tl,mid);
	t[x].r=merge(t[x].r,t[y].r,mid+1,tr);
	modify(x,tl,tr,t[y].rec);
	return x;
}

void dfs(int now,int fa) {
	bool flag=true;
	for (int i=ls[now];i;i=e[i].next) {
		if (e[i].y==fa) continue;
		dfs(e[i].y,now);
		root[now]=merge(root[now],root[e[i].y],-N,N);
		flag=false;
	}
	if (!flag) f[now]=query(root[now],-N,N,a[now]);
	modify(root[now],-N,N,(line) {b[now],f[now]});
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,n) b[i]=read();
	rep(i,2,n) add_edge(read(),read());
	dfs(1,0);
	rep(i,1,n) printf("%lld\n", f[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值