|
| 1 | +/* |
| 2 | +APIO 2013 Toll |
| 3 | +- Firstly, notice that there will always be at least N - K edges that are present in |
| 4 | + the MST for any valid assignment of costs to the new roads |
| 5 | + - Call these edges the "mandatory edges" |
| 6 | +- If we set the costs of the new roads to 0, then we can use Kruskal's to find the |
| 7 | + mandatory edges |
| 8 | +- The use of each new road is determined by the cost of exactly one existing road |
| 9 | + - Call this set of roads the "critical roads" |
| 10 | +- Since they're always present, we can compress the components in the graph with only |
| 11 | + mandatory edges into single nodes, adding the number of people in each component |
| 12 | +- We're therefore left with at most K + 1 compressed nodes and K critical roads |
| 13 | +- By iterating over all possible subsets of new roads that we can force people to |
| 14 | + use (e.g. using a bitmask), we can greedily assign costs to the new roads and take |
| 15 | + the best result |
| 16 | +- Complexity: O(M log M + K * 2^K) |
| 17 | +*/ |
| 18 | + |
| 19 | +#include <bits/stdc++.h> |
| 20 | +#pragma GCC optimize("O3") |
| 21 | +#pragma GCC target("sse4,avx2,fma,avx") |
| 22 | +typedef long long ll; |
| 23 | +using namespace std; |
| 24 | + |
| 25 | +int n, m, k; |
| 26 | +int u[300021], v[300021]; |
| 27 | +ll c[300021], p[100001]; |
| 28 | + |
| 29 | +int cmp1[100001], cmp2[100001], cmp3[100001]; |
| 30 | +inline int find(int A, int *cmp) { return (A == cmp[A] ? A : cmp[A] = find(cmp[A], cmp)); } |
| 31 | +inline void onion(int A, int B, int *cmp) { cmp[find(A, cmp)] = find(B, cmp); } |
| 32 | + |
| 33 | +vector<int> compressed; |
| 34 | +vector<pair<int, ll>> graph[100001]; |
| 35 | +int root; |
| 36 | +vector<tuple<ll, int, int>> critical; |
| 37 | +int tin[100001], tout[100001], timer; |
| 38 | +pair<int, int> par[100001]; |
| 39 | + |
| 40 | +void dfs(int node, int parent = 0) { |
| 41 | + cmp3[node] = node; |
| 42 | + tin[node] = timer++; |
| 43 | + for (int i = 0; i < graph[node].size(); i++) if (graph[node][i].first != parent) { |
| 44 | + dfs(graph[node][i].first, node); |
| 45 | + par[graph[node][i].first] = {node, i}; |
| 46 | + } |
| 47 | + tout[node] = timer; |
| 48 | +} |
| 49 | + |
| 50 | +inline bool is_ancestor(int A, int B) { return tin[A] <= tin[B] && tout[A] >= tout[B]; } |
| 51 | + |
| 52 | +pair<ll, ll> calc(int node, int parent = 0) { |
| 53 | + pair<ll, ll> ans = {0, p[node]}; |
| 54 | + for (pair<int, ll> i : graph[node]) if (i.first != parent) { |
| 55 | + pair<ll, ll> tmp = calc(i.first, node); |
| 56 | + ans.first += tmp.first + tmp.second * i.second; |
| 57 | + ans.second += tmp.second; |
| 58 | + } |
| 59 | + return ans; |
| 60 | +} |
| 61 | + |
| 62 | +ll check(int mask) { |
| 63 | + // Reset the graph and the DSU |
| 64 | + for (int i : compressed) graph[i].clear(), cmp2[i] = i; |
| 65 | + |
| 66 | + // Onion stuff based on mask, and construct some edges in the graph |
| 67 | + for (int i = 1; i <= k; i++) if (mask & (1 << i - 1)) { |
| 68 | + if (find(u[m + i], cmp2) == find(v[m + i], cmp2)) return 0; |
| 69 | + graph[u[m + i]].push_back({v[m + i], 1}); |
| 70 | + graph[v[m + i]].push_back({u[m + i], 1}); |
| 71 | + onion(u[m + i], v[m + i], cmp2); |
| 72 | + } |
| 73 | + |
| 74 | + vector<tuple<ll, int, int>> to_assign; |
| 75 | + for (auto& i : critical) { |
| 76 | + if (find(get<1>(i), cmp2) == find(get<2>(i), cmp2)) to_assign.push_back(i); |
| 77 | + else { |
| 78 | + // Add an edge to the graph |
| 79 | + graph[get<1>(i)].push_back({get<2>(i), 0}); |
| 80 | + graph[get<2>(i)].push_back({get<1>(i), 0}); |
| 81 | + onion(get<1>(i), get<2>(i), cmp2); |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + // Assign weights |
| 86 | + dfs(root); |
| 87 | + for (auto& i : to_assign) { |
| 88 | + get<1>(i) = find(get<1>(i), cmp3); |
| 89 | + while (!is_ancestor(get<1>(i), get<2>(i))) { |
| 90 | + if (graph[par[get<1>(i)].first][par[get<1>(i)].second].second) |
| 91 | + graph[par[get<1>(i)].first][par[get<1>(i)].second].second = get<0>(i); |
| 92 | + onion(get<1>(i), par[get<1>(i)].first, cmp3); |
| 93 | + get<1>(i) = find(get<1>(i), cmp3); |
| 94 | + } |
| 95 | + get<2>(i) = find(get<2>(i), cmp3); |
| 96 | + while (!is_ancestor(get<2>(i), get<1>(i))) { |
| 97 | + if (graph[par[get<2>(i)].first][par[get<2>(i)].second].second) |
| 98 | + graph[par[get<2>(i)].first][par[get<2>(i)].second].second = get<0>(i); |
| 99 | + onion(get<2>(i), par[get<2>(i)].first, cmp3); |
| 100 | + get<2>(i) = find(get<2>(i), cmp3); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + return calc(root).first; |
| 105 | +} |
| 106 | + |
| 107 | +int main() { |
| 108 | + cin.tie(0)->sync_with_stdio(0); |
| 109 | + cin >> n >> m >> k; |
| 110 | + for (int i = 1; i <= m; i++) cin >> u[i] >> v[i] >> c[i]; |
| 111 | + for (int i = 1; i <= k; i++) cin >> u[m + i] >> v[m + i]; |
| 112 | + for (int i = 1; i <= n; i++) cin >> p[i]; |
| 113 | + |
| 114 | + // Find the mandatory edges and collapse components together |
| 115 | + vector<tuple<ll, int, int>> edges; |
| 116 | + for (int i = 1; i <= m + k; i++) edges.push_back({c[i], u[i], v[i]}); |
| 117 | + sort(edges.begin(), edges.end()); |
| 118 | + iota(cmp1 + 1, cmp1 + n + 1, 1); |
| 119 | + iota(cmp2 + 1, cmp2 + n + 1, 1); |
| 120 | + for (auto& i : edges) { |
| 121 | + if (find(get<1>(i), cmp1) != find(get<2>(i), cmp1)) { |
| 122 | + if (get<0>(i)) { |
| 123 | + p[find(get<2>(i), cmp2)] += p[find(get<1>(i), cmp2)]; |
| 124 | + onion(get<1>(i), get<2>(i), cmp2); |
| 125 | + } |
| 126 | + onion(get<1>(i), get<2>(i), cmp1); |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + // Find the K + 1 component parents and the root after compression |
| 131 | + for (int i = 1; i <= n; i++) { |
| 132 | + cmp1[i] = cmp2[i]; |
| 133 | + if (i == cmp2[i]) compressed.push_back(i); |
| 134 | + } |
| 135 | + root = find(1, cmp2); |
| 136 | + // Find the K "critical" edges |
| 137 | + for (auto& i : edges) { |
| 138 | + if (get<0>(i) && find(get<1>(i), cmp1) != find(get<2>(i), cmp1)) { |
| 139 | + critical.push_back(i); |
| 140 | + onion(get<1>(i), get<2>(i), cmp1); |
| 141 | + } |
| 142 | + } |
| 143 | + // Re-index |
| 144 | + for (int i = 1; i <= k; i++) u[m + i] = find(u[m + i], cmp2), v[m + i] = find(v[m + i], cmp2); |
| 145 | + for (auto& i : critical) get<1>(i) = find(get<1>(i), cmp2), get<2>(i) = find(get<2>(i), cmp2); |
| 146 | + |
| 147 | + // Test each subset of edges |
| 148 | + ll ans = 0; |
| 149 | + for (int mask = 1; mask < (1 << k); mask++) ans = max(ans, check(mask)); |
| 150 | + cout << ans; |
| 151 | + return 0; |
| 152 | +} |
0 commit comments