题解 CF932F Escape Through Leaf

link

update 2021.10.13:修正了某些错误。

很有意思的一道题,总算调出来了,分享下自己的思路。

显然可以树形 DP,fi 表示节点 i 的答案,有转移方程fu=min(fv+aubv),vsubtree(u)

这样就有了个 O(n2) 的做法,考虑能否用 DS 优化 DP。

观察到有 fv+aubv 这一形式,若把 fv 视为截距,au 视作要求值的点,bv 视为斜率,发现与一次函数 kx+b 的形式很相似,可以把其看成一条直线。

于是问题转化为在若干直线中,求 x=au 时最小值所对应直线。

维护若干直线某点最值,可以对于每个点,开一棵李超线段树,但若每到一个新节点,都重新建树,复杂度仍不能接受。

mnu(x) 表示对于点 u 的直线集在 x 处的最小值,发现 mnu(x)=min(mnv(x)),vson(u),可以用线段树合并维护最小值,这样做的复杂度可以通过摊到 O(nlogn),具体证明可以看下这篇题解

有很多题解说要平移值域,事实上李超线段树合并不需要平移值域,正常写就好了。

然后直接套板子就好了,我人傻常数大跑了 12.22s

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10, W = 1e5;
int n, a[N], b[N];
ll f[N];
struct edge {
    int to, nxt;
} e[N << 1];
int head[N], cnt;
void add(int u, int v) {
    e[++cnt] = (edge){v, head[u]};
    head[u] = cnt;
}
struct line {
    ll k, b;
    int id;
} t[N << 2];
ll calc(line x, int d) { return x.k * d + x.b; }
ll cross(line x, line y) { return (x.b - y.b) / (y.k - x.k); }
int rt[N * 20], ls[N * 20], rs[N * 20], tot;
void insert(int &x, int l, int r, line s) {
    if(!x) x = ++tot;
    int mid = l + r >> 1;
    if(calc(t[x], mid) > calc(s, mid) || !t[x].id) swap(t[x], s);
    if(l == r || t[x].k == s.k || !s.id) return;
    ll p = cross(t[x], s);
    if(p < l || p > r) return;
    if(s.k > t[x].k) insert(ls[x], l, mid, s);
    else insert(rs[x], mid + 1, r, s);
}
int merge(int x, int y, int l, int r) {
    if(!x || !y) return x + y;
    insert(x, l, r, t[y]);
    int mid = l + r >> 1;
    ls[x] = merge(ls[x], ls[y], l, mid);
    rs[x] = merge(rs[x], rs[y], mid + 1, r);
    return x;
}
line query(int x, int l, int r, int p) {
    if(l == r) return t[x];
    int mid = l + r >> 1;
    line res;
    if(mid >= p) res = query(ls[x], l, mid, p);
    else res = query(rs[x], mid + 1, r, p);
    if(!res.id || calc(t[x], p) < calc(res, p)) return t[x];
    return res;
}
void dfs(int u, int fa) {
    for(int i = head[u], v; i; i = e[i].nxt) {
        v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        rt[u] = merge(rt[u], rt[v], -W, W);
    }
    int mn = query(rt[u], -W, W, a[u]).id;
    f[u] = 1ll * a[u] * b[mn] + f[mn];
    insert(rt[u], -W, W, (line){b[u], f[u], u});
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for(int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
    }
    dfs(1, 0);
    for(int i = 1; i <= n; i++) printf("%lld ", f[i]);
    return 0;
}
posted @   Terac  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示