Live2D

Solution -「UOJ #418」三角形

Description

  Link.

  给定一棵含有 n 个结点的有根树, 点 u 有正权 wu. 每次操作可以:

  • u 上放 wu 枚石子. 必须满足 u 的儿子全部都放了对应数量的石子.
  • 回收结点 u 上的所有石子.

  对于每个点, 求为了在这个点上放上石子, 至少需要准备多少枚石子.

  n2×105.

Solution

  限制条件为 "儿子选完父亲选", 我们知道 "父亲选完儿子选" 有经典的贪心结论, 所以可以反过来考虑决策顺序.

  对于点 u 单独的操作, 可以描述为二元组 (wuwv,w), 表示本次操作需要准备 w 枚石子, 操作完成后石子的变化量为 wuwv. 对于二元组 (a,b),(c,d) 依次合并, 显然有 (a,b)+(c,d)=(a+c,max{b,a+d}). 二元组操作顺序只需要依据操作后需准备石子的数量关系, 取数量最小的一种即可. 这是一个用于贪心的偏序关系.

  正如前文所说, 如果是 "父亲选完选儿子", 可以反复找出全局最优解与父亲合并. 而这里, 我们可把整个决策倒过来. 那么 (a,b)(a,ba), 在按照结论即可合并. 据此模拟出全局最有的操作策略, 在这一顺序上建立线段树, 线段树合并维护每棵子树的策略二元组之和就能求出答案. 复杂度 O(nlogn).

Code

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef long long LL;
typedef std::pair<LL, LL> PLL;
#define fi first
#define se second

template <typename Tp>
inline void chkmin(Tp& u, const Tp& v) { v < u && (u = v, 0); }
template <typename Tp>
inline void chkmax(Tp& u, const Tp& v) { u < v && (u = v, 0); }
template <typename Tp>
inline Tp imin(const Tp& u, const Tp& v) { return u < v ? u : v; }
template <typename Tp>
inline Tp imax(const Tp& u, const Tp& v) { return u < v ? v : u; }

const int MAXN = 2e5;
int n, fa[MAXN + 5], val[MAXN + 5], ord[MAXN + 5], root[MAXN + 5];
std::vector<int> adj[MAXN + 5], fol[MAXN + 5];
bool vis[MAXN + 5];
LL sub[MAXN + 5], ans[MAXN + 5];
PLL wgt[MAXN + 5];

struct DSU {
    int fa[MAXN + 5];
    inline void init() {
        rep (i, 1, n) fa[i] = i;
    }
    inline int find(const int x) {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    inline void unite(const int x, const int y) {
        fa[find(x)] = find(y);
    }
} dsu;

inline PLL operator + (const PLL& u, const PLL& v) {
    return { u.fi + v.fi, imax(u.se, u.fi + v.se) };
}

struct CmpFn {
    inline bool operator () (const int u, const int v) const {
        LL d = (wgt[u] + wgt[v]).se - (wgt[v] + wgt[u]).se;
        if (d) return d < 0;
        if (wgt[u] != wgt[v]) return wgt[u] < wgt[v];
        return u < v;
    }
};
std::set<int, CmpFn> heap;

inline void append(const int u) {
    assert(!vis[u]);
    ord[u] = ++ord[0], vis[u] = true;
    for (int v: fol[u]) append(v);
}

struct SegmentTree {
    static const int MAXND = 4e6;
    int node, ch[MAXND][2]; PLL uni[MAXND];

    inline void pushup(const int u) {
        uni[u] = uni[ch[u][0]] + uni[ch[u][1]];
    }

    inline void merge(int& u, const int v) {
        if (!u || !v) return void(u |= v);
        merge(ch[u][0], ch[v][0]), merge(ch[u][1], ch[v][1]);
        pushup(u);
    }

    inline void insert(int& u, const int l, const int r,
      const int x, const PLL& w) {
        if (!u) u = ++node;
        if (l == r) return void(uni[u] = w);
        int mid = l + r >> 1;
        if (x <= mid) insert(ch[u][0], l, mid, x, w);
        else insert(ch[u][1], mid + 1, r, x, w);
        pushup(u);
    }
} sgt;

inline void solve(const int u) {
    for (int v: adj[u]) solve(v), sgt.merge(root[u], root[v]);
    sgt.insert(root[u], 1, n, ord[u], { sub[u] - val[u], sub[u] });
    ans[u] = (PLL(val[u], val[u]) + sgt.uni[root[u]]).se;
}

int main() {
    scanf("%*d %d", &n);
    rep (i, 2, n) scanf("%d", &fa[i]), adj[fa[i]].push_back(i);
    rep (i, 1, n) scanf("%d", &val[i]), sub[fa[i]] += val[i];

    dsu.init();
    rep (i, 1, n) wgt[i] = { sub[i] - val[i], sub[i] }, heap.insert(i);
    rep (i, 1, n) {
        int u = *heap.begin();
        // fprintf(stderr, "%d\n", u);
        heap.erase(heap.begin());
        if (u == 1 || vis[fa[u]]) append(u);
        else {
            int v = dsu.find(fa[u]);
            heap.erase(v);
            wgt[v] = wgt[v] + wgt[u], dsu.unite(u, v);
            heap.insert(v), fol[v].push_back(u);
        }
    }

    solve(1);
    rep (i, 1, n) printf("%lld%c", ans[i], i < n ? ' ' : '\n');
    return 0;
}

posted @   Rainybunny  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-07-20 Solution -「UNR #5」「UOJ #671」诡异操作
2021-07-20 Solution -「多校联训」区间
点击右上角即可分享
微信分享提示