Skip to content

Commit 4690d64

Browse files
authored
Merge pull request #385 from hitonanode/01-on-tree
01 on Tree
2 parents 91cebf0 + ac6be6e commit 4690d64

File tree

4 files changed

+276
-0
lines changed

4 files changed

+276
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#define PROBLEM \
2+
"https://judge.yosupo.jp/problem/rooted_tree_topological_order_with_minimum_inversions"
3+
#include "../tree_pop_order_optimization.hpp"
4+
#include <iostream>
5+
#include <vector>
6+
using namespace std;
7+
8+
int main() {
9+
cin.tie(nullptr), ios::sync_with_stdio(false);
10+
11+
int N;
12+
cin >> N;
13+
vector<vector<int>> to(N);
14+
15+
for (int i = 1; i < N; ++i) {
16+
int p;
17+
cin >> p;
18+
to.at(p).push_back(i);
19+
to.at(i).push_back(p);
20+
}
21+
22+
vector<long long> c(N), d(N);
23+
for (auto &e : c) cin >> e;
24+
for (auto &e : d) cin >> e;
25+
26+
const auto [order, ret] = Solve01OnTree(to, c, d, 0);
27+
cout << ret << '\n';
28+
for (auto e : order) cout << e << ' ';
29+
cout << '\n';
30+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#define PROBLEM "https://yukicoder.me/problems/no/3148"
2+
#include "../tree_pop_order_optimization.hpp"
3+
4+
#include <algorithm>
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
12+
int N;
13+
string str;
14+
cin >> N >> str;
15+
vector<vector<int>> child(N + 1);
16+
{
17+
vector<int> stk{0};
18+
int openid = 1;
19+
for (auto c : str) {
20+
if (c == '(') {
21+
stk.push_back(openid++);
22+
} else {
23+
int v = stk.back();
24+
stk.pop_back();
25+
if (stk.size()) child.at(stk.back()).push_back(v);
26+
}
27+
}
28+
}
29+
30+
vector<long long> A(N);
31+
for (auto &a : A) cin >> a;
32+
A.insert(A.begin(), 0);
33+
34+
vector<long long> B(A.size(), 1);
35+
B.at(0) = 0;
36+
37+
vector<int> seq = Solve01OnTree(child, A, B, 0).first;
38+
std::reverse(seq.begin(), seq.end());
39+
40+
long long dy = 0, ans = 0;
41+
for (int i : seq) {
42+
if (i == 0) continue;
43+
dy += A.at(i);
44+
ans += dy;
45+
}
46+
47+
cout << ans << '\n';
48+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#pragma once
2+
#include <algorithm>
3+
#include <cassert>
4+
#include <queue>
5+
#include <utility>
6+
#include <vector>
7+
8+
// "01 on Tree"
9+
// https://judge.yosupo.jp/problem/rooted_tree_topological_order_with_minimum_inversions
10+
// https://yukicoder.me/problems/no/3148
11+
template <class S> struct TreePopOrderOptimization {
12+
std::vector<std::vector<int>> to;
13+
std::vector<S> labels;
14+
int root = -1;
15+
std::vector<S> first_slope;
16+
std::vector<int> par;
17+
18+
TreePopOrderOptimization(const std::vector<std::vector<int>> &to, const std::vector<S> &labels,
19+
int root)
20+
: to(to), labels(labels), root(root), first_slope(to.size()), par(to.size(), -1) {
21+
22+
using Pque = std::priority_queue<S, std::vector<S>, std::greater<S>>;
23+
auto rec = [&](auto &&self, int now, int prv) -> Pque {
24+
std::vector<Pque> chs;
25+
26+
for (int nxt : to[now]) {
27+
if (nxt == prv) continue;
28+
assert(par[nxt] == -1);
29+
par[nxt] = now;
30+
chs.emplace_back(self(self, nxt, now));
31+
}
32+
33+
const S &v = labels.at(now);
34+
if (chs.empty()) {
35+
first_slope[now] = v;
36+
Pque pq;
37+
pq.emplace(v);
38+
return pq;
39+
} else {
40+
S first = v;
41+
42+
const int idx = std::max_element(chs.begin(), chs.end(),
43+
[](const auto &a, const auto &b) {
44+
return a.size() < b.size();
45+
}) -
46+
chs.begin();
47+
std::swap(chs[idx], chs.front());
48+
49+
for (int i = 1; i < (int)chs.size(); ++i) {
50+
while (!chs[i].empty()) {
51+
chs.front().emplace(chs[i].top());
52+
chs[i].pop();
53+
}
54+
}
55+
56+
while (!chs.front().empty() and chs.front().top() < first) {
57+
first += chs.front().top();
58+
chs.front().pop();
59+
}
60+
chs.front().emplace(first_slope[now] = first);
61+
return std::move(chs.front());
62+
}
63+
};
64+
65+
rec(rec, root, -1);
66+
}
67+
68+
std::pair<std::vector<int>, S> Solve() const { return SolveSubtree(root); }
69+
70+
// Generate optimal pop order of the subproblem rooted at `r`.
71+
std::pair<std::vector<int>, S> SolveSubtree(int r) const {
72+
using P = std::pair<S, int>;
73+
std::priority_queue<P, std::vector<P>, std::greater<P>> pq;
74+
pq.emplace(first_slope.at(r), r);
75+
76+
std::vector<int> order;
77+
S ret = labels.at(r);
78+
while (!pq.empty()) {
79+
const int idx = pq.top().second;
80+
order.emplace_back(idx);
81+
pq.pop();
82+
if (idx != r) ret += labels.at(idx);
83+
84+
for (int nxt : to.at(idx)) {
85+
if (nxt == par.at(idx)) continue;
86+
pq.emplace(first_slope.at(nxt), nxt);
87+
}
88+
}
89+
90+
return {order, ret};
91+
}
92+
};
93+
94+
template <class T> struct Vector01onTree {
95+
T x, y;
96+
T res;
97+
Vector01onTree(T x, T y) : x(x), y(y), res(0) {}
98+
Vector01onTree() : x(0), y(0), res(0) {}
99+
bool operator<(const Vector01onTree &r) const {
100+
if (x == 0 and y == 0) return false;
101+
if (r.x == 0 and r.y == 0) return true;
102+
if (x == 0 and r.x == 0) return y < r.y;
103+
if (x == 0) return false;
104+
if (r.x == 0) return true;
105+
return y * r.x < x * r.y; // be careful of overflow
106+
}
107+
bool operator>(const Vector01onTree &r) const { return r < *this; }
108+
109+
void operator+=(const Vector01onTree &r) {
110+
res += r.res + y * r.x;
111+
x += r.x;
112+
y += r.y;
113+
}
114+
};
115+
116+
template <class T>
117+
std::pair<std::vector<int>, T>
118+
Solve01OnTree(const std::vector<std::vector<int>> &to, const std::vector<T> &xs,
119+
const std::vector<T> &ys, int root) {
120+
121+
const int n = to.size();
122+
std::vector<Vector01onTree<T>> labels;
123+
for (int i = 0; i < n; ++i) labels.emplace_back(xs.at(i), ys.at(i));
124+
125+
const TreePopOrderOptimization<Vector01onTree<T>> tpo(to, labels, root);
126+
auto [order, all_prod] = tpo.Solve();
127+
return {order, all_prod.res};
128+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: Tree pop order optimization / "01 on Tree" (木の根から 2 次元ベクトルや 01 文字列などを pop する順列に関する最小化)
3+
documentation_of: ./tree_pop_order_optimization.hpp
4+
---
5+
6+
いわゆる "01 on Tree" を解く.
7+
8+
## 使用方法
9+
10+
### オーソドックスな "01 on Tree"
11+
12+
```cpp
13+
int N;
14+
vector<vector<int>> to(N);
15+
16+
vector<long long> x(N), y(N);
17+
18+
const auto [order, inversions] = Solve01OnTree(to, x, y, 0);
19+
cout << inversions << '\n';
20+
for (auto e : order) cout << e << ' ';
21+
```
22+
23+
### より一般的な構造
24+
25+
以下は [28147번: Fail Fast](https://www.acmicpc.net/problem/28147) の例.まず各頂点が持つデータを表現する構造体を定義する. `operator+=` の内容に注意せよ.
26+
27+
```cpp
28+
struct S {
29+
double x, y;
30+
S(double x, double y) : x(x), y(y) {}
31+
S() : x(0), y(0) {}
32+
bool operator<(const S &r) const {
33+
if (x == 0 and y == 0) return false;
34+
if (r.x == 0 and r.y == 0) return true;
35+
if (x == 0 and r.x == 0) return y < r.y;
36+
if (x == 0) return false;
37+
if (r.x == 0) return true;
38+
return y * r.x < x * r.y; // be careful of overflow
39+
}
40+
bool operator>(const S &r) const { return r < *this; }
41+
42+
void operator+=(const S &r) {
43+
y += r.y * (1 - x);
44+
x = x + (1 - x) * r.x;
45+
}
46+
};
47+
```
48+
49+
あとは,以下のように `TreePopOrderOptimization()` を呼んでやればよい.戻り値の `.first` は最適な pop 順の順列, `.second` はその順に `S` の元の総積を( `+=` で)とったときの値である.
50+
51+
```cpp
52+
vector<vector<int>> to(N);
53+
vector<S> vals(N);
54+
55+
auto [seq, all_prod] = TreePopOrderOptimization(to, vals, 0).Solve();
56+
```
57+
58+
## 問題例
59+
60+
- [Library Checker: Rooted Tree Topological Order with Minimum Inversions](https://judge.yosupo.jp/problem/rooted_tree_topological_order_with_minimum_inversions)
61+
- [AtCoder Grand Contest 023 F - 01 on Tree](https://atcoder.jp/contests/agc023/tasks/agc023_f)
62+
- [AtCoder Beginner Contest 376 G - Treasure Hunting](https://atcoder.jp/contests/abc376/tasks/abc376_g)
63+
- [No.3148 Min-Cost Destruction of Parentheses - yukicoder](https://yukicoder.me/problems/no/3148)
64+
- [28147번: Fail Fast](https://www.acmicpc.net/problem/28147)
65+
- 通常の 01 on Tree とは異なる演算の入ったデータ構造を載せるタイプの問題.
66+
67+
## リンク
68+
69+
- [解説 - AtCoder Beginner Contest 376(Promotion of AtCoder Career Design DAY)](https://atcoder.jp/contests/abc376/editorial/11196)
70+
- [241203_01 on Tree](https://acompany-ac.notion.site/241203_01-on-Tree-151269d8558680b2b639d7bfcbff2b20)

0 commit comments

Comments
 (0)