传送门


\(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. Linux高可靠技术

    1.进程挂死时,有后台监控程序检测重新拉起. 2.进程占用系统资源超过ulimit限定的资源时,会被ulimit杀死,同时配合后台监控程序,重新拉起进程,实现进程可靠性. 3.Linux系统的高可靠性 ...

  2. loadrunner 结果分析-loadrunner结果分析

    结果分析-loadrunner结果分析 by:授客 QQ:1033553122 百度网盘分享链接: 烦请 复制一下网址到浏览器中打开,输入密码提取 链接: http://pan.baidu.com/s ...

  3. python进程间通信--信号Signal

    信号signal 是python进程间通信多种机制中的其中一种机制.可以对操作系统进程的控制,当进程中发生某种原因而中断时,可以异步处理这个异常. 信号通过注册的方式‘挂’在一个进程中,并且不会阻塞该 ...

  4. [20180317]12c TABLE ACCESS BY INDEX ROWID BATCHED.txt

    [20180317]12c TABLE ACCESS BY INDEX ROWID BATCHED.txt --//简单探究12c TABLE ACCESS BY INDEX ROWID BATCHE ...

  5. [20171120]关于find 软连接问题.txt

    [20171120]关于find 软连接问题.txt --//上个星期为了测试oracle参数filesystemio_options,将数据库做了一次移动.但是我使用find对软链接目录查询时--/ ...

  6. python第三十六天-----类中的特殊成员方法

    __doc__ 查看尖的描述信息 __module__表示当前操作的对象所在的模块 __class__表示当前操作的对象所属的类 __init__构造方法 通过类创建对象自动执行 __del__析构方 ...

  7. scrapy爬取校花网男神图片保存到本地

    爬虫四部曲,本人按自己的步骤来写,可能有很多漏洞,望各位大神指点指点 1.创建项目 scrapy startproject xiaohuawang scrapy.cfg: 项目的配置文件xiaohua ...

  8. HDU ACM 1869 六度分离(Floyd)

    六度分离 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  9. 10 种机器学习算法的要点(附 Python 和 R 代码)

    本文由 伯乐在线 - Agatha 翻译,唐尤华 校稿.未经许可,禁止转载!英文出处:SUNIL RAY.欢迎加入翻译组. 前言 谷歌董事长施密特曾说过:虽然谷歌的无人驾驶汽车和机器人受到了许多媒体关 ...

  10. VS2015安装与C++进行简单单元测试

    1:VS2015是微软最新发布的编译器,http://www.itellyou.cn/这是我们的北航大神助教提供的下载网址,以前我们都是自己在网上找,找到的总不是那么如意,这下大神助教提供的网址就好好 ...