原题链接:http://codeforces.com/problemset/problem/932/F
大致题意:给出一颗以1号节点为根节点的树,以及每个点上的点权ai和bi,每个点可以跳到其子树中的任意一个节点j,跳跃的代价是ai*bj,多次跳跃的代价为每次跳跃的代价之和。问每个节点跳到叶子节点的最小代价。
一开始思路不太明显,如果尝试记忆化搜索,显然会TLE。于是可以把公式列出来看一下,令f[i]为从节点i跳到叶子节点的最小代价和,则有如下公式
f[i]=min{f[j]+a[i]×b[j]} (j是i的子树中某一个节点)
如果从第i号节点跳跃到第j号节点的代价 比 跳到第k号节点更优,则会有如下公式
f[j]+a[i]×b[j]<f[k]+a[i]×b[k]
稍作移项就可以得到(假设b[j]< b[k])
a[i]>f[k]−f[j]b[j]−b[k]
再做一下变形即得
a[i]>f[j]−f[k](−b[j])−(−b[k])
这样就是一个类似于斜率优化dp的公式,每个点以-b[i]为横坐标,以f[i]为纵坐标,那么每个点的最优决策点一定在其子树以这种坐标定义构成的凸壳上。(准确的说是下凸壳,随着横坐标增大,每个点与后一节点构成的斜率也会增大,这个性质接下来会有用)
于是只要在每个节点,将其子树内的凸包都合并为一个凸包,再来一次二分斜率就可以得到这个节点的f值,然后把这个点也加入凸包就可以了。
主要思路定下来了,如何在log的时间内完成这些操作呢。
只需要一个multiset就可以了。
因为是multiset,所以可以在Log的时间内按照横坐标大小插入凸壳,然后维护下凸壳的性质
然后是找到不大于某个值的最大斜率在哪,这个似乎会很棘手,只能暴力在multiset里一个一个找,但是,由于这是一个下凸壳,各个点之间的横坐标的大小关系和对前一个点的斜率的大小关系是等价的,于是利用multiset的lower_bound函数来找到大于等于这个值的斜率所在的点,其前一个点就是最优决策点。 \cancel其实直接暴力找也是可以在CF上AC,但似乎可能可以构造出数据卡掉,只是可能不是很好构造数据
同时我这里为了避免被卡精度,利用查询lower_bound的参数一定是个整数,把小数的斜率转化为了整数的斜率,如1.1转化为1,-1.5转化为-2,-1转化为-1,1转化为1。
代码:
#include<bits/stdc++.h>
#define MAXN 1100000
using namespace std;
typedef long long LL;
inline void read(long long &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
LL n;
LL a[ MAXN ],b[ MAXN ],f[ MAXN ];
vector<LL> son[ MAXN ];
int flag;
struct zy{
mutable LL a,b;
mutable LL xl;
zy(){}
zy(LL a, LL b ,LL tmp):a(a),b(b),xl(tmp){}
bool operator < (const zy& B)const { return flag?xl<B.xl:a<B.a; }
bool operator > (const zy& B)const { return flag?xl>B.xl:a>B.a; }
};
const LL inf = 1e18;
struct Tubao:multiset<zy>{
LL get_xl(zy A,zy B){
if ( A.a==B.a )
return B.b<A.b?-inf:inf;
LL x=(B.b-A.b),y=(B.a-A.a);
LL tmp=x/y;
if(( (x>0) && ( y>0 ) )|| ( (x<0) && ( y<0 ) )||(x%y==0) )
tmp++;
tmp--;
return tmp;
}
void add(zy ksd){
iterator tmp=insert(ksd);
if ( tmp!=begin() )
{
iterator now=tmp;
iterator pre=tmp;
pre--;
now->xl=get_xl( *pre , *now );
iterator nex=tmp;
nex++;
if ( (nex!=end() )&& ( nex->xl <= now->xl ) )
{
erase(now);
return ;
}
while ( pre->xl >= now->xl )
{
if ( pre==begin() )
{
erase(pre);
break;
}
erase(pre);
pre=now;
pre--;
now->xl=get_xl( *pre , *now );
}
}
else
tmp->xl=-inf;
iterator nex=tmp;
iterator now=tmp;
nex++;
if ( nex!=end() )
{
nex->xl=get_xl( *now , *nex );
now=nex;
nex++;
if ( nex!=end() )
{
while ( now->xl >= nex->xl )
{
erase(now);
now=nex;
nex++;
now->xl=get_xl( *tmp , *now );
if ( nex==end() )
break;
}
}
}
}
LL get_f(LL st){
flag=1;
iterator tmp=lower_bound(zy(0,0,st));
flag=0;
tmp--;
LL T_ans=-tmp->a*st+tmp->b;
return T_ans;
}
}now;
void dfs(int x,int fa,Tubao &now){
Tubao tmp;
bool l_op=1;
for (auto y:son[x])
if ( y!=fa )
{
l_op=0;
tmp.clear();
dfs( y , x , tmp);
for (auto s:tmp)
now.add(zy(s.a,s.b,0));
}
if ( l_op )
f[x]=0;
else
f[x]=now.get_f( a[x] );
now.add(zy(-b[x],f[x],0));
}
int main(){
read(n);
for (int i=1;i<=n;i++)
read(a[i]);
for (int i=1;i<=n;i++)
read(b[i]);
for (int i=1;i<n;i++)
{
LL x,y;
read(x);read(y);
son[x].push_back(y);
son[y].push_back(x);
}
dfs(1,0,now);
for (int i=1;i<=n;i++)
printf("%I64d ",f[i]);
return 0;
}