【CF932F】Escape Through Leaf

题意:给你一棵n个点的树,每个点有树形ai和bi,如果x是y的祖先,则你可以从x花费$a_x\times b_y$的费用走到y(费用可以为负)。对于每个点,求从这个点开始走到某个叶子节点的最小费用。

$n\le 10^5,|a_i|,|b_i|\le 10^5$

题解:用f[x]表示x的答案,显然f[x]=min{f[y]+a[x]*b[y]}是一个凸包,我们可以用set维护凸包,到时候自底向上做一次启发式合并就行了(也可以线段树合并)。

用叉积会爆long long差评~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
const int maxn=100010;
typedef long long ll;
struct node
{
ll x,y;
node () {}
node (ll a,ll b) {x=a,y=b;}
bool operator < (const node &a) const {return (x==a.x)?(y<a.y):(x<a.x);}
long double operator * (const node &a) const {return (long double)x*a.y-(long double)y*a.x;}
node operator - (const node &a) const {return node(x-a.x,y-a.y);}
};
set<node> s[maxn];
set<node>::iterator it,iit;
int n,cnt,tot;
int to[maxn<<1],nxt[maxn<<1],head[maxn],siz[maxn],rt[maxn];
ll va[maxn],vb[maxn],f[maxn];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
inline void add(int a,int b)
{
to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
inline void insert(int a,node x)
{
it=s[a].lower_bound(x);
if(it!=s[a].end()&&(*it).x==x.x) s[a].erase(it),it=s[a].lower_bound(x);
if(it!=s[a].begin())
{
it--;
if((*it).x==x.x) return ;
it++;
}
node l,r;
if(it!=s[a].end()&&it!=s[a].begin())
{
r=*it,it--,l=*it;
if((x-l)*(r-x)<=0) return ;
}
while(1)
{
it=s[a].lower_bound(x);
if(it==s[a].end()) break;
l=*it,it++;
if(it==s[a].end()) break;
r=*it;
if((l-x)*(r-l)<=0) s[a].erase(l);
else break;
}
while(1)
{
it=s[a].lower_bound(x);
if(it==s[a].begin()) break;
it--,r=*it;
if(it==s[a].begin()) break;
it--,l=*it;
if((r-l)*(x-r)<=0) s[a].erase(r);
else break;
}
s[a].insert(x);
}
inline int merge(int a,int b)
{
if(s[a].size()<s[b].size()) swap(a,b);
for(iit=s[b].begin();iit!=s[b].end();iit++) insert(a,*iit);
s[b].clear();
return a;
}
void dfs(int x,int fa)
{
rt[x]=++tot;
for(int i=head[x],y;i!=-1;i=nxt[i]) if(to[i]!=fa)
{
y=to[i],dfs(y,x),rt[x]=merge(rt[x],rt[y]);
}
if(!s[rt[x]].size()) f[x]=0;
else
{
int l=-100001,r=100001,mid;
node a,b;
while(l<r)
{
mid=(l+r)>>1;
it=s[rt[x]].lower_bound(node(mid,-1ll<<60));
if(it==s[rt[x]].begin())
{
l=mid+1;
continue;
}
if(it==s[rt[x]].end())
{
r=mid;
continue;
}
b=*it,it--,a=*it;
if((b.y-a.y)<=-va[x]*(b.x-a.x)) l=mid+1;
else r=mid;
}
it=s[rt[x]].lower_bound(node(l-1,-1ll<<60));
f[x]=va[x]*(*it).x+(*it).y;
}
insert(rt[x],node(vb[x],f[x]));
}
int main()
{
//freopen("cf932F.in","r",stdin);
//freopen("cf932F.out","w",stdout);
n=rd();
int i,a,b;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++) va[i]=rd();
for(i=1;i<=n;i++) vb[i]=rd();
for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a);
dfs(1,0);
for(i=1;i<=n;i++) printf("%lld ",f[i]);
return 0;
}//3 1 -1 1 -1 -1 -1 1 2 2 3

【CF932F】Escape Through Leaf 启发式合并set维护凸包的更多相关文章

  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\) 设\(f_i\)表示第\(i\)个节点的答案,\(S_i\)表示\(i\)的子节点集合,那么转移方程为\(f_i = \min\limits_{j \in S_i} \{a_i ...

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

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

  4. Luogu5290 十二省联考2019春节十二响(贪心+启发式合并)

    考虑链的做法,显然将两部分各自从大到小排序后逐位取max即可,最后将根计入.猜想树上做法相同,即按上述方式逐个合并子树,最后加入根.用multiset启发式合并即可维护.因为每次合并后较小集合会消失, ...

  5. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)

    题面 传送门 题解 为什么成天有人想搞些大新闻 这里写的是\(yyb\)巨巨说的启发式合并的做法(虽然\(LCT\)的做法不知道比它快到哪里去了--) 建出\(SAM\),那么两个前缀的最长公共后缀就 ...

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

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

  7. 【bzoj3510】首都 LCT维护子树信息(+启发式合并)

    题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...

  8. 【BZOJ3123】森林(主席树,启发式合并)

    题意:一个带点权的森林,要求维护以下操作: 1.询问路径上的点权K大值 2.两点之间连边 n,m<=80000 思路:如果树的结构不发生变化只需要维护DFS序 现在因为树的结构发生变化,要将两棵 ...

  9. BZOJ2888 资源运输(LCT启发式合并)

    这道题目太神啦! 我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去.那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器--启发式 ...

随机推荐

  1. 【WP8】仿QQ提示消息

    WP版的QQ提示消息的时候从顶部滑入,3秒后从顶部滑出,本文模仿该效果实现一个MessageToastManager类用于显示提示消息 思路很简单,就是动画而已,支持配置颜色和回掉 // ****** ...

  2. Objective-C语法之NSPredicate的使用

    正则表达式判断手机号码和电话号码的方法: #import <Foundation/Foundation.h> /** 正则判断手机号码地址格式 */ BOOL isMobileNumber ...

  3. .NET条形码

    建议不要用CODE-39码,改用CODE-128码: CODE-39码密度比较低,条码数字内容太多,导致条码太长,缩短长度就只能减小X尺寸,造成识读困难: CODE-128码密度高,相同的数字生成条码 ...

  4. [转]jmeter 自定义测试脚本

    http://blog.csdn.net/kash_chen007/article/details/37690411 http://wangym.iteye.com/blog/731729 1.创建一 ...

  5. tomcat启动时设定环境变量

    在tomcat的bin目录中修改startup.bat 设置CATALINA_HOME set "CATALINA_HOME=F:\solr\apache-tomcat\apache-tom ...

  6. 在window的cmd窗口下运行linux命令

    之前看很多视频老师都是用Linux命令操作命令框,感觉很方便,自己在cmd窗口试了一下,所有这些命令都提示不是内部或外部命令,后来发现了windows还有一个powershell命令行工具,用起来似乎 ...

  7. lua正则表达式如何匹配中文

    function CheckChinese(s) local ret = {}; local f = '[%z\1-\127\194-\244][\128-\191]*'; local line, l ...

  8. python中,如何将两个变量值进行拼接

    说明: 字符串和字符串之间可以拼接,那么变量和变量之间如何进行拼接,在此记录下. 操作过程: 1.通过加号 + 操作符,将两个变量拼接在一起 >>> prefix = 'p' > ...

  9. OpenCV学习笔记:opencv_highgui模块

    一,简介 本模块为跨平台的gui/IO组件,支持平台包括windows,linux,mac,IOS,android,可支持图像/视频/摄像头的读取显示以及转码. 二,分析 本模块为跨平台的gui/IO ...

  10. Matlab 随机数字

    1.随机生成仅仅有0.1元素的矩阵(m行n列) A=round(rand(m,n)) 2.随机生成每行有若干个0,1元素的矩阵,比方每行仅仅有2个元素为1,其它元素为0 A=zeros(4,5) fo ...