@description@

给定一个 n 个点的树(标号1~n),以结点 1 为根。每个结点有两个点权 ai 与 bi。

你可以从一个点出发跳到它的子树中的某个结点去(不能跳到自己)。

从 x 跳到 y 所花费的代价为 ax * by,跳跃的总代价为每次跳跃的代价之和。

对于每个结点,计算从它出发跳到某一叶子结点的最小代价和。

Input

第一行包含一个整数 n (2 ≤ n ≤ 10^5),表示树中的结点数量。

第二行包含 n 个整数 a1, a2, ..., an (-105≤ai≤105)。

第三行包含 n 个整数 b1, b2, ..., bn (-105≤bi≤105)。

接下来 n-1 行每行包含两个整数 ui 和 vi (1≤ui, vi≤n),描述了树中的一条边。

Output

输出 n 个空格分开的整数,第 i 个描述了从第 i 个结点跳到叶子结点的最小代价和。

Examples

Input

3

2 10 -1

7 -7 5

2 3

2 1

Output

10 50 0

Input

4

5 -10 5 7

-8 -80 -3 -10

2 1

2 4

1 3

Output

-300 100 0 0

@solution@

本题方法很多,可以转成 dfs 序然后写 cdq 分治,可以写平衡树在树上启发式合并维护凸包,也可以像我一开始一样转成 dfs 序分块维护凸包(它竟然没有 TLE。。。令我颇感意外)。

在这里介绍一个不那么传统的方法吧:我们使用李超树 + 线段树合并。

首先不难写出 dp 式 dp[x] = min(dp[c] + ax*bc),发现它是斜率优化的形式。

处理斜率优化问题,除了传统的方法将其看作凸包以外,其实还有李超线段树的方法。

我们记 bc 为斜率,dp[c] 为截距,ax 为横坐标,可以通过李超线段树找到最小值(可自行百度)。

类比于平衡树的启发式合并,我们可以直接用经典的线段树合并,在树上对李超线段树进行合并操作。

与平常的线段树合并不同的是,当两棵线段树的结点同时都含有直线标记,要将一个直线标记插入到另一棵线段树中。

复杂度感觉像是 O(nlogn)?每个直线标记最多在线段树中被下放 log 次,而线段树合并的复杂度 <= 一个一个将结点插入的复杂度。

但是感觉跑起来没有 O(nlogn) 那么快?不是很清楚是常数问题还是时间复杂度证错了。

@accepted code@

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
const ll INF = (1LL<<60);
struct line{
ll k, b;
line(ll _k=0, ll _b=0) : k(_k), b(_b) {}
ll get(ll x) {return k * x + b;}
};
ll a[MAXN + 5], b[MAXN + 5], f[MAXN + 5];
struct segtree{
struct node{
line l;
node *ch[2];
}pl[60*MAXN + 5], *ncnt, *NIL;
segtree() {
NIL = ncnt = pl;
NIL->ch[0] = NIL->ch[1] = NIL;
}
node *newnode(line l) {
node *p = (++ncnt);
p->ch[0] = p->ch[1] = NIL, p->l = l;
return p;
}
void insert(node *&rt, int l, int r, line ln) {
if( rt == NIL ) {
rt = newnode(ln);
return ;
}
int m = (int)floor(1.0*(l + r)/2);
if( rt->l.get(l) > ln.get(l) ) swap(rt->l, ln);
if( rt->l.get(m) > ln.get(m) )
swap(rt->l, ln), insert(rt->ch[0], l, m, ln);
else if( rt->l.get(r) > ln.get(r) )
insert(rt->ch[1], m + 1, r, ln);
}
ll query(node *rt, int l, int r, ll p) {
if( rt == NIL ) return INF;
if( l == r ) return rt->l.get(p);
int m = (int)floor(1.0*(l + r)/2);
if( p <= m ) return min(rt->l.get(p), query(rt->ch[0], l, m, p));
else return min(rt->l.get(p), query(rt->ch[1], m + 1, r, p));
}
node *merge(node *rt1, node *rt2, int l, int r) {
if( rt1 == NIL ) return rt2;
if( rt2 == NIL ) return rt1;
int m = (int)floor(1.0*(l + r)/2);
rt1->ch[0] = merge(rt1->ch[0], rt2->ch[0], l, m);
rt1->ch[1] = merge(rt1->ch[1], rt2->ch[1], m + 1, r);
insert(rt1, l, r, rt2->l);
return rt1;
}
}T;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
segtree::node *rt[MAXN + 5];
void dfs(int x, int fa) {
rt[x] = T.NIL;
for(edge *p=adj[x];p;p=p->nxt)
if( p->to != fa )
dfs(p->to, x), rt[x] = T.merge(rt[x], rt[p->to], -MAXN, MAXN);
if( rt[x] == T.NIL ) f[x] = 0;
else f[x] = T.query(rt[x], -MAXN, MAXN, a[x]);
T.insert(rt[x], -MAXN, MAXN, line(b[x], f[x]));
}
int main() {
int n; scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%lld", &a[i]);
for(int i=1;i<=n;i++) scanf("%lld", &b[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs(1, 0);
for(int i=1;i<=n;i++) printf("%lld%c", f[i], i == n ? '\n' : ' ');
}

@details@

现在只写了分块维护凸包与李超线段树合并两种方法。

有时间(咕咕咕)练一下另外两种吧。

@codeforces - 932F@ Escape Through Leaf的更多相关文章

  1. 932F Escape Through Leaf

    传送门 题目大意 https://www.luogu.org/problemnew/show/CF932F 分析 我们可以从叶子向根每次插入b和ans 所以我们不难发现就是相当于插入线段 于是李超树+ ...

  2. Codeforces 932.F Escape Through Leaf

    F. Escape Through Leaf time limit per test 3 seconds memory limit per test 256 megabytes input stand ...

  3. 【CF932F】Escape Through Leaf 启发式合并set维护凸包

    [CF932F]Escape Through Leaf 题意:给你一棵n个点的树,每个点有树形ai和bi,如果x是y的祖先,则你可以从x花费$a_x\times b_y$的费用走到y(费用可以为负). ...

  4. CF932F Escape Through Leaf

    CF932F Escape Through Leaf 首先, $ O(n^2) $ dp 是很显然的,方程长这样: \[dp[u] = min\{dp[v] + a_u\times b_v\} \] ...

  5. Codeforces Round #463 F. Escape Through Leaf (李超线段树合并)

    听说正解是啥 set启发式合并+维护凸包+二分 根本不会啊 , 只会 李超线段树合并 啦 ... 题意 给你一颗有 \(n\) 个点的树 , 每个节点有两个权值 \(a_i, b_i\) . 从 \( ...

  6. CodeForces 148B Escape

    Escape Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Stat ...

  7. CF932F Escape Through Leaf 斜率优化、启发式合并

    传送门 \(DP\) 设\(f_i\)表示第\(i\)个节点的答案,\(S_i\)表示\(i\)的子节点集合,那么转移方程为\(f_i = \min\limits_{j \in S_i} \{a_i ...

  8. CF932F Escape Through Leaf(DP,斜率优化)

    SB 题. 写出 DP 方程:\(f_i\) 表示从 \(i\) 跳的最小值. \(i\) 是叶子就是 \(0\),否则就是选个子树中的 \(v\),\(f_i=\min(f_v+a_ib_v)\). ...

  9. 【CF 463F】Escape Through Leaf

    题意 给你一棵 \(n\) 个点的树,每个节点有两个权值 \(a_i,b_i\). 从一个点 \(u\) 可以跳到以其为根的子树内的任意一点 \(v\)(不能跳到 \(u\) 自己),代价是 \(a_ ...

随机推荐

  1. mybatis学习:mybatis注解开发一对一的查询配置

    实体类: public class Account { private Integer id; private Integer uid; private Double money; private U ...

  2. 干货来了!2019阿里云合作伙伴峰会SaaS加速器专场回顾合集:嘉宾分享、深度解读

    2019年7月26日,在上海举办的阿里云合作伙伴峰会上,阿里云正式发布SaaS生态战略,计划用阿里云的品牌.渠道.资本.方法论.技术加持伙伴,成就亿级营收独角兽. 该生态战略计划招募10家一级SaaS ...

  3. Visual Studio 2019 正式发布

    原文链接: https://www.oschina.net/news/105629/vs2019-general-availability 如约而至,微软已于今天推出 Visual Studio 20 ...

  4. Cors之带凭据的请求

    带凭据的请求 默认情况下,跨源请求不提供凭据.通过将withCredentials属性设置为true,可以制定某个请求应该发送凭据.

  5. Java过滤器—Filter用法简介

    一.什么是Filter? Filter译为过滤器. 由于年,Sun公司在Servlet2.3规范中添加了Filter功能,并在Servlet2.4中对Filter进行了细节上的补充. 二.运行原理: ...

  6. Docker搭建ELK的javaweb应用日志收集存储分析系统

    1.启动elasticsearch docker run -d --name myes -p 9200:9200 elasticsearch:2.3 2.启动kibana docker run --n ...

  7. LINUX普通猫的拔号工具介绍

    普通猫分为串口和PCI的,请查看 <关于网络设备概述 > 普通猫的拔号工具主要有kppp和wvdial:在Redhat/Fedora中,用system-config-network 或re ...

  8. LinqToExcel 简洁与优美开源库

    转载:https://www.cnblogs.com/codefish/archive/2013/04/08/3009098.html 正在做项目,同事问道有啥简单的方法读取excel到DataTab ...

  9. select2 如何自定义提示信息-布布扣-bubuko.com

    标签:color   dom   layui   href   默认事件   替换   each   ase   options 最近项目中使用到了select2来美化和丰富下拉框的功能,这款插件功能 ...

  10. oracle 控制结构

    1.if 逻辑结构 if/then 结构是最简单的条件测试,如果条件为真,则执行程序的一行或者多行,如果条件为假,则什么都不执行, 示例: if  1>2 then null; end if ; ...