题目
题目链接:https://codeforces.com/problemset/problem/932/F
有一颗 \(n\) 个节点的树(节点从 \(1\) 到 \(n\) 依次编号)。每个节点有两个权值,第i个节点的权值为 \(a_i,b_i\)。
你可以从一个节点跳到它的任意一个子节点上。从节点 \(x\) 跳到节点 \(y\) 一次的花费为 \(a_x\times b_y\)。跳跃多次走过一条路径的总费用为每次跳跃的费用之和。请分别计算出每个节点到达树的每个叶子节点的费用中的最小值。
思路
设 \(f[x]\) 表示点 \(x\) 跳到叶子的最小费用,显然有
\[f[x]=\min_{y\in\mathrm{sub}(x)}(f[y]+a[x]b[y]) \]发现这个东西就是在其子树内的若干斜率为 \(b\),截距为 \(a\) 的直线中取 \(x=a\) 时最小值。无脑上 dsu on tree 和李超线段树即可。
时间复杂度 \(O(n\log^2 n)\)。
事实上根据洛谷题解上的证明,李超线段树合并可以做到 \(O(n\log n)\) 的复杂度。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,K=100001;
int n,tot,rt,head[N],son[N],size[N];
ll f[N],a[N],b[N];
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs1(int x,int fa)
{
size[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa)
{
dfs1(v,x);
size[x]+=size[v];
if (size[v]>size[son[x]]) son[x]=v;
}
}
}
ll calc(int k,int x)
{
return b[k]*(x-K)+f[k];
}
struct SegTree
{
int tot,ans[N*4],lc[N*4],rc[N*4];
int update(int x,int l,int r,int k)
{
if (!x) x=++tot;
if (l==r)
{
if (!ans[x] || calc(k,l)<calc(ans[x],l)) ans[x]=k;
return x;
}
int mid=(l+r)>>1;
if (!ans[x] || (calc(k,l)<=calc(ans[x],l) && calc(k,r)<=calc(ans[x],r)))
{
ans[x]=k;
return x;
}
if (calc(k,l)>calc(ans[x],l) && calc(k,r)>calc(ans[x],r))
return x;
if (b[k]>b[ans[x]])
{
if (calc(k,mid)<=calc(ans[x],mid))
rc[x]=update(rc[x],mid+1,r,ans[x]),ans[x]=k;
else
lc[x]=update(lc[x],l,mid,k);
return x;
}
if (b[k]<b[ans[x]])
{
if (calc(k,mid)<=calc(ans[x],mid))
lc[x]=update(lc[x],l,mid,ans[x]),ans[x]=k;
else
rc[x]=update(rc[x],mid+1,r,k);
return x;
}
return x;
}
ll query(int x,int l,int r,int k)
{
if (!x) return 1e18;
if (l==r) return calc(ans[x],k);
int mid=(l+r)>>1;
if (k<=mid) return min(calc(ans[x],k),query(lc[x],l,mid,k));
else return min(calc(ans[x],k),query(rc[x],mid+1,r,k));
}
void clr(int x)
{
if (lc[x]) clr(lc[x]);
if (rc[x]) clr(rc[x]);
lc[x]=rc[x]=ans[x]=0;
}
}seg;
void dfs3(int x,int fa)
{
rt=seg.update(rt,1,K*2,x);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs3(v,x);
}
}
void dfs2(int x,int fa,bool flag)
{
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa && v!=son[x]) dfs2(v,x,0);
}
if (son[x]) dfs2(son[x],x,1);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa && v!=son[x]) dfs3(v,x);
}
if (size[x]==1) f[x]=0;
else f[x]=seg.query(rt,1,K*2,a[x]+K);
if (!flag) seg.clr(rt),seg.tot=rt=0;
else rt=seg.update(rt,1,K*2,x);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs1(1,0); dfs2(1,0,0);
for (int i=1;i<=n;i++)
printf("%lld ",f[i]);
return 0;
}