传送门


\(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-微信公众平台开发最佳实践

    一,微信是由腾讯公司广州研发中心产品团队开发,该团队经理张小龙被称为“微信之父”,公司总裁马化腾确定该产品名称为“微信”. 二,常见问题及解决方案. 1,请求URL超时. 这种情况一般是由于服务器网速 ...

  2. mysql 优化配置参数(my.cnf)

    max_connections:允许客户端并发连接的最大数量,默认值是151,一般将该参数设置为500-2000max_connect_errors:如果客户端尝试连接的错误数量超过这个参数设置的值, ...

  3. drupal 2006 mysql server has gone away

    在开发一个cms drupal网站时遇到了如上图的错误,几经百度谷歌,都一致说需要修改mysql的配置 max_allowed_packet参数,但是由于我买的是虚拟主机,并没有权限修改. 本来已经放 ...

  4. Configure Monit on AWS CentOS7 to guard Squid proxy

    Install Monit:sudo -iamazon-linux-extras install epelyum -y install monit Config monit: vim /etc/mon ...

  5. postgresql-JSON使用

    json,jsonb区别 json和jsonb,而两者唯一的区别在于效率,json是对输入的完整拷贝,使用时再去解析,所以它会保留输入的空格,重复键以及顺序等.而jsonb是解析输入后保存的二进制,它 ...

  6. godaddy.com 都转到 www.dnspod.cn

    我们在godaddy上注册了域名,但因为godaddy对域名解析的诸多限制和缓慢. 所以必需把域名服务器更改为dnspod上,然后再在dnspod上做A记录,CNAME等设置都很方便.

  7. February 6th, 2018 Week 6th Tuesday

    To be is to be perceived. 存在即被感知. How to interpret this quote? Maybe it means that everything in you ...

  8. C# 响应微信发送的Token验证,文字、图文自动回复、请求客服对话.....

    代码如下,有需要的可以参考: using System; using System.Collections.Generic; using System.Linq; using System.Web; ...

  9. Mysqldumpslow的用法汇总

    mysqldumpslow --help可显示其参数的使用 经常使用的参数: -s,是order的顺序 al   平均锁定时间 ar   平均返回记录时间 at   平均查询时间(默认) c    计 ...

  10. Django之Form进阶

    s6day76 内容回顾: Form组件:  - 类  继承Form  - 字段  字段  - 实例化  - is_valid()  - cleaned_data  - errors Form 组件: ...