結果
問題 |
No.3194 Do Optimize Your Solution
|
ユーザー |
👑 ![]() |
提出日時 | 2025-06-24 17:56:00 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
MLE
|
実行時間 | - |
コード長 | 6,570 bytes |
コンパイル時間 | 5,142 ms |
コンパイル使用メモリ | 339,500 KB |
実行使用メモリ | 630,112 KB |
最終ジャッジ日時 | 2025-06-27 20:51:37 |
合計ジャッジ時間 | 23,885 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge1 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 2 |
other | AC * 9 MLE * 1 -- * 7 |
ソースコード
#ifdef DEBUG #include <range/v3/all.hpp> #endif #include <bits/stdc++.h> using namespace std; using u32 = uint32_t; using u64 = uint64_t; template<class T> using V = pmr::vector<T>; /* 木 A の辺ごとに寄与を数える. A を根付き木とする. A の頂点 v 以下の部分木に含まれる頂点の index の集合を S とする.B[S] に重み 1 を,それ以外の頂点に重み 0 を割り振ることを考える. 以下のデータ構造があれば良い. add(v, w): 頂点 v に重み w を追加する. get(v): sum(dist(v, u) * w[u] for u in V) を求める. 重心分解をすると,log 1 個になる HL 分解し,heavy-path を下から順に, */ void solve() { #ifdef DEBUG namespace views = ::ranges::views; namespace ranges = std::ranges; #endif // 入力 u64 N; cin >> N; // 実質的に CSR な読み込み vector<pair<u32, u32>> edge(N - 1); vector<u32> deg(N); for (auto& [u, v] : edge) { cin >> u >> v; u--; v--; deg[u]++; deg[v]++; } vector<V<u32>> A(N); for (auto&& [a, d] : views::zip(A, deg)) a.reserve(d); for (auto [u, v] : edge) { A[u].push_back(v); A[v].push_back(u); } deg.assign(N, 0); for (auto& [u, v] : edge) { cin >> u >> v; u--; v--; deg[u]++; deg[v]++; } vector<V<u32>> B(N); for (auto&& [a, d] : views::zip(B, deg)) a.reserve(d); for (auto [u, v] : edge) { B[u].push_back(v); B[v].push_back(u); } // A を HL 分解 // heavy-edge は G[*][0] に vector<u32> siz(N, 1), heavy_parent(N, -1); auto hld = [&](auto dfs, u32 v) -> void { if (A[v].empty()) return; for (u32 i : A[v]) { erase(A[i], v); dfs(dfs, i); siz[v] += siz[i]; } auto p = ranges::max_element(A[v], {}, [&](u32 i) { return siz[i]; }); if (v) heavy_parent[*p] = v; // 根はやらなくて良い swap(*p, A[v][0]); }; hld(hld, 0); // B を重心分解 struct T { u64 cnt = 0, sum = 0; }; vector<V<T>> sum_dist(N); for (auto&& [b, s] : views::zip(B, sum_dist)) s.resize(b.size() + 1, {}); vector<V<tuple<T*, T*, u64>>> centroid_ancestor(N); // {連結成分の和,重心からその方向の和,重心までの距離} for (auto& c : centroid_ancestor) c.reserve(bit_width(N)); { vector<bool> deleted(N, false); vector<pair<u32, u32>> cc; // {index, 部分木サイズ} auto adj = [&](u32 v) { return B[v] | views::filter([&](u32 u) { return !deleted[u]; }); }; auto dfs_size = [&](auto dfs_size, u32 parent, u32 v) -> u32 { // 頂点 v の部分木のサイズを求めながら,頂点のリストを作る u32 siz = 1; for (u32 i : adj(v)) if (i != parent) { siz += dfs_size(dfs_size, v, i); } cc.emplace_back(v, siz); return siz; }; auto CD = [&](auto CD, u32 v) -> void { // 頂点 v を含む連結成分を重心分解する cc.clear(); u32 half_siz = dfs_size(dfs_size, -1, v) / 2; const u32 cen = ranges::max(cc, {}, [&](const auto& p) { return p.second <= half_siz ? p.second : 0; }).first; // centroid_ancestor を更新 auto& S = sum_dist[cen]; T* const p_sum = &S.back(); centroid_ancestor[cen].emplace_back(p_sum, nullptr, 0); for (auto&& [i, s] : views::zip(B[cen], S)) { if (deleted[i]) continue; T* const q_sum = &s; auto dfs = [&](auto dfs, u32 parent, u32 v, u32 dist) -> void { centroid_ancestor[v].emplace_back(p_sum, q_sum, dist); for (u32 u : adj(v)) if (u != parent) { dfs(dfs, v, u, dist + 1); } }; dfs(dfs, cen, i, 1); } // 重心を削除 deleted[cen] = true; // 子を重心分解 for (u32 u : adj(cen)) CD(CD, u); }; CD(CD, 0); } // B の全頂点に 0 をセット for (auto& anc : centroid_ancestor) { for (auto& [p_sum, q_sum, dist] : anc) { auto& P = *p_sum; P.cnt++; P.sum += dist; if (q_sum == nullptr) break; auto& Q = *q_sum; Q.cnt++; Q.sum += dist; } } vector<bool> val(N); u64 ans = 0, sum01 = 0; auto add = [&](u32 v, int x) -> void { if (val[v]) assert(x == -1); else assert(x == 1); val[v] = !val[v]; // B で頂点 v の重みを 1 に / 0 に for (auto& [p_sum, q_sum, dist] : centroid_ancestor[v]) { auto& P = *p_sum; P.cnt -= x; P.sum -= x * dist; sum01 += x * (P.sum + P.cnt * dist); P.cnt -= x; P.sum -= x * dist; if (q_sum == nullptr) break; auto& Q = *q_sum; Q.cnt -= x; Q.sum -= x * dist; sum01 -= x * (Q.sum + Q.cnt * dist); Q.cnt -= x; Q.sum -= x * dist; } }; auto set_subtree = [&](auto set_subtree, u32 v) -> void { // 部分木の重みをすべて 1 に add(v, 1); for (u32 i : A[v]) { set_subtree(set_subtree, i); } }; auto reset_subtree = [&](auto reset_subtree, u32 v) -> void { // 部分木の重みをすべて 0 に add(v, -1); for (u32 i : A[v]) { reset_subtree(reset_subtree, i); } }; auto climb = [&](auto climb, u32 v) -> void { // 部分木の重みをすべて 1 に add(v, 1); for (u32 i : A[v] | views::drop(1)) { set_subtree(set_subtree, i); } ans += sum01; if (u32 p = heavy_parent[v]; p != -1) { // heavy-edge を登る climb(climb, p); } else { reset_subtree(reset_subtree, v); } }; // heavy-path を下から順に処理 for (auto&& [i, a] : A | views::enumerate) { if (a.size()) continue; climb(climb, (u32)i); } // 出力 cout << ans * 2 << '\n'; } int main() { pmr::monotonic_buffer_resource mr{ 256 << 20 }; pmr::set_default_resource(&mr); cin.tie(0)->sync_with_stdio(0); solve(); }