传送门


\(DP\)

设\(f_i\)表示第\(i\)个节点的答案,\(S_i\)表示\(i\)的子节点集合,那么转移方程为\(f_i = \min\limits_{j \in S_i} \{a_i \times b_j + f_j\}\)

这是一个很明显的斜率优化式子,斜率为\(b_j\),截距为\(f_j\),自变量为\(a_i\)。考虑到斜率没有单调性,所以使用set维护凸包。

使用set维护凸包比较简单。一条直线插入时,先判断这条线段在当前凸包中是否合法,然后不断把两边不合法的直线删去。具体的实现看下面代码的insert函数吧

然后如何将一个点所有儿子的set合并为自己的set呢?使用启发式合并来保证复杂度。

最后,知道了当前点的所有儿子节点的凸包,如何计算当前点的答案?正解是二分斜率,因为你没法在set上直接二分直线。

总复杂度为\(O(nlog^2n)\)

还可以通过dfn序将树上问题转化为序列问题,对一个点有贡献的是一段连续区间,就可以用CDQ分治解决。

#include<bits/stdc++.h>
#define int long long
#define ld long double
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
bool f = 0;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
} const int MAXN = 100010;
struct Edge{
int end , upEd;
}Ed[MAXN << 1];
struct node{
int k , b;
}now;
set < node > s[MAXN];
long long sum[MAXN] , a[MAXN] , b[MAXN] , minN[MAXN] , fa[MAXN] , size[MAXN] , head[MAXN] , N , cntEd; bool operator <(node a , node b){
return a.k < b.k || (a.k == b.k && a.b < b.b);
} inline void addEd(int a , int b){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
} void dfs(int now , int f){
fa[now] = f;
size[now] = 1;
for(int i = head[now] ; i ; i = Ed[i].upEd)
if(Ed[i].end != f){
dfs(Ed[i].end , now);
size[now] += size[Ed[i].end];
}
if(size[now] == 1)
minN[now] = 0;
} inline ld calcNode(node a , node b){
return (a.b - b.b) / (ld)(b.k - a.k);
} inline void insert(int now , int k , int b){//插入一条斜率为k、截距为b的直线
node x , l , r;
x.k = k;
x.b = b;
set < node > :: iterator it = s[now].lower_bound(x);
if(it != s[now].end() && (*it).k == k){//判断是否存在斜率相同的直线
s[now].erase(it);
it = s[now].lower_bound(x);
}
else
if(it != s[now].begin() && (*--it).k == k)
return;
it = s[now].lower_bound(x);
if(it != s[now].begin() && it != s[now].end()){
l = *it , r = *(--it);
if(calcNode(r , x) < calcNode(l , r) && calcNode(l , r) < calcNode(l , x))//判断这一条直线是否能够被加入当前凸包
return;
++it;
}
while(1){//向两边删去不合法直线
it = s[now].lower_bound(x);
if(it == s[now].end())
break;
l = *it;
if(++it == s[now].end())
break;
r = *it;
if(calcNode(l , r) > calcNode(l , x) && calcNode(l , r) > calcNode(r , x))
s[now].erase(--it);
else
break;
}
while(1){
it = s[now].lower_bound(x);
if(it == s[now].begin())
break;
l = *(--it);
if(it == s[now].begin())
break;
r = *(--it);
if(calcNode(l , r) < calcNode(l , x) && calcNode(l , r) < calcNode(r , x))
s[now].erase(++it);
else
break;
}
s[now].insert(x);
} inline void merge(int root , int now){
bool f = 0;
if(size[root] < size[now]){
swap(root , now);
f = 1;
}
for(set < node > :: iterator it = s[now].begin() ; it != s[now].end() ; ++it)
insert(root , (*it).k , (*it).b);
if(f){
s[now] = s[root];
swap(now , root);
}
s[now].clear();
} void dsu(int now){//别被误导了,这个不是dsu on tree
if(size[now] == 1){
insert(now , b[now] , minN[now]);
return;
}
for(int i = head[now] ; i ; i = Ed[i].upEd)
if(Ed[i].end != fa[now]){
dsu(Ed[i].end);
merge(now , Ed[i].end);
}
int l = -1e5 - 1 , r = 1e5 + 1;
set < node > :: iterator it;
node L , R;
while(l < r){
int mid = l + r >> 1;
it = s[now].lower_bound((node){mid , (long long)-1e15});
if(it == s[now].begin()){
l = mid + 1;
continue;
}
if(it == s[now].end()){
r = mid;
continue;
}
L = *it;
R = *(--it);
if(R.k * a[now] + R.b >= L.k * a[now] + L.b)
l = mid + 1;
else
r = mid;
}
L = *s[now].lower_bound((node){l - 1 , (long long)-1e15});
minN[now] = L.k * a[now] + L.b;
insert(now , b[now] , minN[now]);
} signed main(){
N = read();
memset(minN , 0x3f , sizeof(minN));
for(int i = 1 ; i <= N ; i++)
a[i] = read();
for(int i = 1 ; i <= N ; i++)
b[i] = read();
for(int i = 1 ; i < N ; i++){
int a = read() , b = read();
addEd(a , b);
addEd(b , a);
}
dfs(1 , 0);
dsu(1);
for(int i = 1 ; i <= N ; i++)
cout << minN[i] << ' ';
return 0;
}

CF932F Escape Through Leaf 斜率优化、启发式合并的更多相关文章

  1. CF932F Escape Through Leaf

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

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

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

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

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

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

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

  5. [多校 NOIP 联合模拟 20201130 T4] ZZH 的旅行(斜率优化dp,启发式合并,平衡树)

    题面 题目背景 因为出题人天天被 ZZH(Zou ZHen) 吊打,所以这场比赛的题目中出现了 ZZH . 简要题面 数据范围 题解 (笔者写两个log的平衡树和启发式合并卡过的,不足为奇) 首先,很 ...

  6. Codeforces 932.F Escape Through Leaf

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

  7. 【CF 463F】Escape Through Leaf

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

  8. @codeforces - 932F@ Escape Through Leaf

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个 n 个点的树(标号1~n),以结点 1 为根.每个结点 ...

  9. [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

随机推荐

  1. 【读书笔记】iOS-Settings Bundle

    一,打开工程--->右键--->Add File--->iOS-->Resource-->Settings Bundle. 二,会弹出如下对话框---->Creat ...

  2. Python 基于urllib.request封装http协议类

    基于urllib.request封装http协议类 by:授客QQ:1033553122 测试环境: Python版本:Python 3.3   代码实践 #!/usr/bin/env python ...

  3. LyX使用中的一些问题

    编译开始产生的检查错误 试用LyX2.3,在2.15中能编译通过的文档,竟然提示错误 The user preamble of your document contains glyphs that a ...

  4. python同步原语--线程锁

    多线程锁是python多种同步原语中的其中一种.首先解析一下什么是同步原语,python因为GIL(全局解析锁)的缘故,并没有真正的多线性.另外python的多线程存在一个问题,在多线程编程时,会出现 ...

  5. Java:【面向对象:抽象类,接口】

    本文内容: 抽象类 接口 抽象类与接口的异同 首发日期:2018-03-24 抽象类: 虽然已经有了父类,但有时候父类也是无法直接描述某些共有属性的,比如哺乳类和人类都会叫,而一般来说哺乳类这个父类并 ...

  6. python之with语句的原理

    首发时间:2018-02-23 15:28 之前看到一篇博客说博主python面试时遇到面试官提问with的原理,而那位博主的博文没有提及with原理,故有此文. 关于with语句,官方文档中是这样描 ...

  7. Microsoft .NET Core 1.0.0 VS 2015 Tooling Preview 2 Uninstall Failed

    卸载过程中总是卸载失败报0x80070001:函数不明确错误.转遍了各大论坛和QQ,最终还是在stackoverflow上找到了答案... 原因是我卸载时选择的DotNetCore.1.0.0-VS2 ...

  8. python第三十五天-----作业完成--学校选课系统

    选课系统:角色:学校.学员.课程.讲师要求:1. 创建北京.上海 2 所学校2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开3. 课程包含, ...

  9. 阿里云上安装pip3(Ubuntu)

    安装pip3: 这个简单啊,到网上下载get-pip.py的脚本,然后scp到你的阿里云服务器上,python3 get-pip.py即可. 如果不会scp,哈哈,按照下面的几步: wget http ...

  10. [MapReduce_7] MapReduce 中的排序

    0. 说明 部分排序 && 全排序 && 采样 && 二次排序 1. 介绍 sort 是根据 Key 进行排序 [部分排序] 在每个分区中,分别进行排序 ...