|
| 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 | +} |
0 commit comments