algo — C++23 日本語リファレンス GitHub

test/yukicoder/1718.test.cpp

AC · 最終更新 2026-10-23 · GitHub

問題: https://yukicoder.me/problems/no/1718

依存するヘッダ(3)
実装
// competitive-verifier: PROBLEM https://yukicoder.me/problems/no/1718
#include <algorithm>
#include <iostream>
#include <vector>
#include "graph/graph.hpp"
#include "tree/rerooting.hpp"

// 各始点 X について、全どんぐり頂点を訪れる最小移動数を求める。
// 戻る必要はないので、答 = 2 * |Steiner木(どんぐり ∪ {X}) の辺数| - max(X からどんぐりまでの距離)。
//   - 2 * 辺数 は全辺を往復する分、最後に一番遠いどんぐりへ向かう枝だけ片道で済むので max を引く。

// E(X) = Steiner木(どんぐり ∪ {X}) の辺数。集約型 ESum と確定値型 EKey を分ける 2 型モノイド。
struct ESum {
    bool has;         // この方向(複数方向の集約)にどんぐりがあるか
    long long edges;  // この方向が X の Steiner 木に寄与する辺数
};
struct EKey {
    bool has;     // この部分木にどんぐりがあるか
    long long s;  // この部分木の内部にある Steiner 辺数
};
struct EMonoid {
    using value_type = ESum;
    using key_type = EKey;
    static ESum id() { return {false, 0}; }
    static ESum op(const ESum &a, const ESum &b) { return {a.has || b.has, a.edges + b.edges}; }
    // 辺を渡る: 子部分木にどんぐりがあれば「辺 1 本 + 内部の Steiner 辺」が寄与する。
    template <class W>
    static ESum f(const EKey &k, W) {
        return {k.has, k.has ? k.s + 1 : 0};
    }
    // 頂点で確定: 自身がどんぐりなら has を立てる。内部 Steiner 辺数は子方向の寄与の総和。
    static EKey g(const ESum &s, int is_acorn) { return {is_acorn != 0 || s.has, s.edges}; }
};

// maxdist(X) = X から最も遠いどんぐりまでの距離(辺数)。max モノイド。
struct DMonoid {
    using value_type = long long;
    static constexpr long long NEG = -(1LL << 60);  // どんぐり無しの番兵
    static long long id() { return NEG; }
    static long long op(long long a, long long b) { return std::max(a, b); }
    template <class W>
    static long long f(long long v, W) {
        return v + 1;
    }
    static long long g(long long v, int is_acorn) { return std::max(v, is_acorn != 0 ? 0LL : NEG); }
};

int main() {
    int n, k;
    std::cin >> n >> k;
    csr_graph<void> g(n);
    g.input_edges(n - 1);  // 1-indexed・重みなし無向辺
    std::vector<int> acorn(n, 0);
    for (int i = 0; i < k; ++i) {
        int d;
        std::cin >> d;
        acorn[d - 1] = 1;
    }

    Rerooting<EMonoid, csr_graph<void>, int> rr_e(g, acorn);
    Rerooting<DMonoid, csr_graph<void>, int> rr_d(g, acorn);
    for (int x = 0; x < n; ++x) std::cout << 2 * rr_e[x].s - rr_d[x] << '\n';

    return 0;
}