Skip to content

Commit d23a444

Browse files
authored
Merge pull request #278 from marsenis/37b-4-tiphunting
37b-4-tiphunting
2 parents 173f8dd + 18fd446 commit d23a444

File tree

9 files changed

+1064
-4
lines changed

9 files changed

+1064
-4
lines changed

_data/contests/37-PDP.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ tiphunting:
8888
statement_pdf_url: "https://drive.google.com/file/d/1tFvY_3m4KCz4ldE3vyLLsidE-PYt1hQs/view"
8989
statement_md: true
9090
testcases_url: ""
91-
solution: false
92-
solution_author: ""
93-
codes_in_git: false
94-
solution_tags: []
91+
solution: true
92+
solution_author: "Μάκης Αρσένης"
93+
codes_in_git: true
94+
solution_tags: [lca, dfs, trees, binary lifting]
9595
on_judge: false
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
TASK(
2+
name = "tiphunting",
3+
test_count = 37,
4+
files_dir = "testdata/37-PDP/tiphunting/",
5+
input_file = "tiphunting.in",
6+
output_file = "tiphunting.out",
7+
time_limit = 2,
8+
mem_limit = 128,
9+
solutions = [
10+
SOLUTION(
11+
name = "subtask1",
12+
source = "subtask1.cc",
13+
passes_only = [2, 3, 4, 5],
14+
lang = "c++",
15+
),
16+
SOLUTION(
17+
name = "subtask2",
18+
source = "subtask2.cc",
19+
passes_only = [6, 7, 8, 9, 10],
20+
lang = "c++",
21+
),
22+
SOLUTION(
23+
name = "subtask3",
24+
source = "subtask3.cc",
25+
passes_only = [1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
26+
lang = "c++",
27+
),
28+
SOLUTION(
29+
name = "subtask4",
30+
source = "subtask4.cc",
31+
passes_only = [6, 7, 8, 9, 10, 16, 17, 18, 19, 20, 21],
32+
lang = "c++",
33+
),
34+
SOLUTION(
35+
name = "subtask5",
36+
source = "subtask5.cc",
37+
passes_only = [22, 23, 24, 25, 26, 27],
38+
lang = "c++",
39+
),
40+
SOLUTION(
41+
name = "subtask6",
42+
source = "optimal.cc",
43+
passes_all,
44+
lang = "c++",
45+
),
46+
]
47+
)
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#include <cstdio>
2+
#include <cassert>
3+
#include <vector>
4+
#include <cmath>
5+
6+
using namespace std;
7+
8+
using ll = pair<long, long>;
9+
using lll = tuple<long, long, long>;
10+
using vvll = vector<vector<ll>>;
11+
using vl = vector<long>;
12+
using vvl = vector<vector<long>>;
13+
14+
vvll tree;
15+
vl tip, depth, parent, parent_weight;
16+
vvl pred;
17+
vector<long long> best_subtree_tour, best_supertree_tour, best_supertree_root_walk;
18+
19+
long long positive_part(long long x) { return max(0LL, x); }
20+
21+
// Αρχικοποίηση όλων των global vectors για ένα δέντρο με `n` κορυφές.
22+
void init(int n) {
23+
// ceil(log2(max_N))
24+
constexpr int kMaxH = 18;
25+
26+
tip.resize(n);
27+
tree.resize(n);
28+
29+
// Αρχικοποιώντας `depth[0] = 0`, `parent[0] = 0` θέτουμε την κορυφή
30+
// 0 ως ρίζα του δέντρου. Η συνάρτηση `compute_auxiliary` συμπληρώνει
31+
// τις τιμές και για τους υπόλοιπους κόμβους.
32+
depth.resize(n, 0);
33+
parent.resize(n, 0);
34+
parent_weight.resize(n, 0);
35+
36+
pred.resize(kMaxH, vector<long>(n));
37+
best_subtree_tour.resize(n);
38+
best_supertree_tour.resize(n);
39+
best_supertree_root_walk.resize(n);
40+
}
41+
42+
// Διασχίζει το δέντρο `tree` ξεκινώντας από την κορυφή `u` και υπολογίζει
43+
// αναδρομικά τις τιμές `depth[v]`, `parent[v]` και `parent_weight[v]` για κάθε
44+
// κορυφή `v != u` στο υποδέντρο της `u`. Οι τιμές `depth[u]`, `parent[u]` και
45+
// `parent_weight[u]` θα πρέπει να έχουν ήδη υπολογισθεί από τον caller.
46+
//
47+
// `depth[u]`: Το βάθος του `u` στο δέντρο, το οποίο ορίζεται ως το πλήθος των
48+
// ακμών στο μονοπάτι από τον `u` προς τη ρίζα. Για παράδειγμα το βάθος της
49+
// ρίζας είναι 0. `parent[u]`: Ο γονέας του `u`. `parent_weight[u]`: Κόστος
50+
// του δρόμου που συνδέει τον `u` με τον γονέα του.
51+
void compute_auxiliary(int u) {
52+
for (auto [v, w]: tree[u]) {
53+
if (v == parent[u]) continue;
54+
parent[v] = u;
55+
parent_weight[v] = w;
56+
depth[v] = depth[u] + 1;
57+
58+
compute_auxiliary(v);
59+
}
60+
}
61+
62+
// Διασχίζει το δέντρο `tree` και υπολογίζει αναδρομικά τις τιμές
63+
// `best_subtree_tour` για την κορυφή `u` κι όλους τους απογόνους της.
64+
//
65+
// `best_subtree_tour[u]`: Το κέρδος της βέλτιστης διαδρομής η οποία ξεκινάει
66+
// και καταλήγει πάλι πίσω στο `u`, παραμένοντας στο υποδέντρο που ορίζει η
67+
// κορυφή `u`. Mε άλλα λόγια, η διαδρομή απαγορεύεται να διασχίσει τον δρόμο
68+
// `(u, parent)`.
69+
void compute_best_subtree_tour(long u) {
70+
best_subtree_tour[u] = tip[u];
71+
72+
for (auto [v, w]: tree[u]) {
73+
if (v == parent[u]) continue;
74+
compute_best_subtree_tour(v);
75+
best_subtree_tour[u] += positive_part(best_subtree_tour[v] - 2*w);
76+
}
77+
}
78+
79+
// Διασχίζει το δέντρο `tree` και υπολογίζει αναδρομικά τις τιμές
80+
// `subtree_root_opt` για την κορυφή `u` κι όλους τους απογόνους της,
81+
// χρησιμοποιώντας τις τιμές `best_subtree_tour` που υπολογίσαμε ήδη στην
82+
// προηγούμενη διάσχιση.
83+
//
84+
// `best_supertree_root_walk[u]`: Το κέρδος της βέλτιστης διαδρομής η οποία
85+
// ξεκινάει από την κορυφή `u`, καταλήγει στη ρίζα του δέντρου και μένει πάντα
86+
// ΕΚΤΟΣ του υποδέντρου που ορίζει η `u`. Το φιλοδώρημα της κορυφής `u` ΔΕΝ
87+
// προσμετράται.
88+
void compute_best_supertree_root_walk(long u) {
89+
best_supertree_root_walk[u] = 0;
90+
91+
// Αν η κορυφή `u` ΔΕΝ είναι ρίζα.
92+
if (parent[u] != u)
93+
best_supertree_root_walk[u] =
94+
best_subtree_tour[parent[u]] + best_supertree_root_walk[parent[u]]
95+
- positive_part(best_subtree_tour[u] - 2*parent_weight[u]) - parent_weight[u];
96+
97+
for (auto [v, w]: tree[u])
98+
if (v != parent[u])
99+
compute_best_supertree_root_walk(v);
100+
}
101+
102+
// Διασχίζει το δέντρο `tree` και υπολογίζει αναδρομικά τις τιμές
103+
// `best_subtree_tour` για την κορυφή `u` κι όλους τους απογόνους της,
104+
// χρησιμοποιώντας τις τιμές `best_subtree_tour` που υπολογίσαμε ήδη στην
105+
// προηγούμενη διάσχιση.
106+
//
107+
// best_supertree_tour[u] = κέρδος της βέλτιστης διαδρομής η οποία ξεκινάει αλλά
108+
// ΚΑΙ καταλήγει στην κορυφή `u`, και μένει πάντα ΕΚΤΟΣ του υποδέντρου που
109+
// ορίζει η `u`. Το φιλοδώρημα της κορυφής `u` ΔΕΝ προσμετράται.
110+
void compute_best_supertree_tour(int u) {
111+
best_supertree_tour[u] = 0;
112+
113+
// Αν η κορυφή `u` ΔΕΝ είναι ρίζα.
114+
if (parent[u] != u)
115+
best_supertree_tour[u] =
116+
positive_part(best_subtree_tour[parent[u]] + best_supertree_tour[parent[u]]
117+
- positive_part(best_subtree_tour[u] - 2*parent_weight[u]) - 2*parent_weight[u]);
118+
119+
for (auto [v, w]: tree[u])
120+
if (v != parent[u])
121+
compute_best_supertree_tour(v);
122+
}
123+
124+
// Υπολογίζει τον πίνακα `pred` έτσι ώστε για κάθε 0 <= h <= H, 0 <= u < N:
125+
// `pred[h][u] == v` αν και μόνο αν ο `v` είναι ο `2^h`-πρόγονος του `u`.
126+
// Για παράδειγμα `pred[0][u] == parent[u]` γιατί ο γονέας του $u$
127+
// είναι ο `2^0 = 1`-ος πρόγονός του.
128+
// O caller θα πρέπει να έχει ήδη υπολογίσει τον πίνακα parent
129+
// (δες `compute_auxiliary`) έτσι ώστε η τιμή `parent[u]` να είναι
130+
// ο γονέας της `u`, εκτός από την ρίζα `r` για την οποία `r == parent[r]`.
131+
void compute_pred() {
132+
const long n = parent.size();
133+
const long H = pred.size() - 1;
134+
135+
for (long u = 0; u < n; ++u)
136+
pred[0][u] = parent[u];
137+
138+
for (long h = 1; h <= H; ++h)
139+
for (long u = 0; u < n; ++u)
140+
pred[h][u] = pred[h - 1][pred[h - 1][u]];
141+
}
142+
143+
// Υπολογίζει τρεις τιμές `(z, a, b)` όπου `z` είναι ο Ελάχιστος Κοινός Πρόγονος
144+
// (LCA) των `u, v`, η `a` είναι η μοναδική κορυφή στο μονοπάτι από `a` προς `z`
145+
// για την οποία `parent[u] == z` (ή `a == -1` αν τέτοια κορυφή δεν υπάρχει) και
146+
// αντίστοιχα `b` είναι η μοναδική κορυφή στο μονοπάτι από `z` προς `v` τέτοια
147+
// ώστε `parent[v] == z` (ή `b == -1` αν τέτοια κορυφή δεν υπάρχει).
148+
//
149+
// Η συνάρτηση χρησιμοποιεί τον πίνακα `pred` που υπολόγισε νωρίτερα η
150+
// συνάρτηση `compute_pred` καθώς και τον πίνακα `depth` που υπολόγισε νωρίτερα
151+
// η συνάρτηση `compute_auxiliary`.
152+
lll lca(long u, long v) {
153+
const long H = pred.size() - 1;
154+
155+
if (u == v)
156+
return {u, -1, -1};
157+
158+
if (depth[u] < depth[v]) {
159+
auto [w, i, j] = lca(v, u);
160+
return {w, j, i};
161+
}
162+
163+
if (depth[u] != depth[v]) {
164+
for (long h = H; h >= 0; h--)
165+
if (depth[ pred[h][u] ] > depth[v])
166+
u = pred[h][u];
167+
168+
if (pred[0][u] == v)
169+
return { v, u, -1 };
170+
171+
u = pred[0][u];
172+
}
173+
174+
for (long h = H; h >= 0; --h) {
175+
if (pred[h][u] != pred[h][v]) {
176+
u = pred[h][u];
177+
v = pred[h][v];
178+
}
179+
}
180+
181+
return { pred[0][u], u, v };
182+
}
183+
184+
int main() {
185+
int subtask;
186+
scanf("%i", &subtask);
187+
188+
long n, q;
189+
scanf("%li%li", &n, &q);
190+
191+
init(n);
192+
193+
for (long i = 0; i < n; ++i)
194+
scanf("%li", &tip[i]);
195+
196+
// Αναπαράσταση του δέντρου με adjacency list:
197+
// To `tree[u]` περιέχει ένα vector με pairs `(v, w)` για κάθε κορυφή `v` που
198+
// συνδέεται με τη `u` με κόστός `w`.
199+
for (long i = 0; i < n-1; ++i) {
200+
long u, v, w;
201+
scanf("%li%li%li", &u, &v, &w);
202+
203+
tree[u-1].push_back({v-1, w});
204+
tree[v-1].push_back({u-1, w});
205+
}
206+
207+
compute_auxiliary(0);
208+
209+
compute_pred();
210+
211+
compute_best_subtree_tour(0);
212+
compute_best_supertree_tour(0);
213+
compute_best_supertree_root_walk(0);
214+
215+
for (long i = 0; i < q; ++i) {
216+
long L, R;
217+
scanf("%li%li", &L, &R);
218+
L--, R--;
219+
220+
if (L == R) {
221+
printf("%lli\n", best_subtree_tour[L] + best_supertree_tour[L]);
222+
continue;
223+
}
224+
225+
auto [z, u, v] = lca(L, R);
226+
assert(u != -1 || v != -1);
227+
228+
long long sol = 0;
229+
if (u == -1) {
230+
// Η κορυφή `L` είναι πρόγονος της `R`.
231+
assert(z == L);
232+
sol = best_supertree_root_walk[R] - best_supertree_root_walk[L]
233+
+ best_supertree_tour[L] + best_subtree_tour[R];
234+
} else if (v == -1) {
235+
// Η κορυφή `R` είναι πρόγονος της `L`.
236+
assert(z == R);
237+
sol = best_supertree_root_walk[L] - best_supertree_root_walk[R]
238+
+ best_supertree_tour[R] + best_subtree_tour[L];
239+
} else {
240+
// Οι κορυφές `L, R` έχουν κοινό πρόγονο τον `z != L, R`.
241+
assert(pred[0][u] == z);
242+
assert(pred[0][v] == z);
243+
244+
sol =
245+
best_supertree_root_walk[L] - best_supertree_root_walk[u] + best_subtree_tour[L] // (a)
246+
+ best_supertree_root_walk[R] - best_supertree_root_walk[v] + best_subtree_tour[R] // (b)
247+
+ best_subtree_tour[z] // (c1)
248+
- positive_part(best_subtree_tour[u] - 2*parent_weight[u]) // (c2)
249+
- positive_part(best_subtree_tour[v] - 2*parent_weight[v]) // (c3)
250+
+ best_supertree_tour[z] // (d)
251+
- (parent_weight[u] + parent_weight[v]); // (e)
252+
}
253+
254+
printf("%lli\n", sol);
255+
}
256+
257+
return 0;
258+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <cstdio>
2+
#include <cassert>
3+
4+
using namespace std;
5+
6+
int main() {
7+
int subtask;
8+
scanf("%i", &subtask);
9+
assert(subtask == 1);
10+
11+
long n, q;
12+
scanf("%li%li", &n, &q);
13+
14+
long long sol = 0;
15+
for (long i = 0; i < n; ++i) {
16+
long t;
17+
scanf("%li", &t);
18+
sol += t;
19+
}
20+
21+
for (long i = 0; i < n-1; ++i) {
22+
long tmp1, tmp2, tmp3;
23+
scanf("%li%li%li", &tmp1, &tmp2, &tmp3);
24+
}
25+
26+
for (long i = 0; i < q; ++i) {
27+
long L, R;
28+
scanf("%li%li", &L, &R);
29+
printf("%lli\n", sol);
30+
}
31+
32+
return 0;
33+
}

0 commit comments

Comments
 (0)