Skip to content

Commit ad80596

Browse files
committed
APIO 13 Toll
1 parent 1e9ec9c commit ad80596

File tree

2 files changed

+156
-3
lines changed

2 files changed

+156
-3
lines changed

APIO/APIO 13-toll.cpp

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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+
}

IOI/IOI 16-shortcut.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ using namespace std;
4141

4242
const ll INF = 1e16;
4343

44+
ll p[1000000], half_plane[4];
45+
pair<ll, int> ord1[1000000], ord2[1000000];
46+
4447
ll find_shortcut(int n, vector<int> l, vector<int> d, int c) {
45-
vector<ll> p(n, 0);
4648
for (int i = 1; i < n; i++) p[i] = p[i - 1] + l[i - 1];
4749

4850
// Order by d[i] + p[i] and d[i] - p[i]
49-
vector<pair<ll, int>> ord1(n), ord2(n);
5051
for (int i = 0; i < n; i++)
5152
ord1[i] = {d[i] + p[i], i}, ord2[i] = {d[i] - p[i], i};
5253
sort(ord1, ord1 + n);
@@ -58,7 +59,7 @@ ll find_shortcut(int n, vector<int> l, vector<int> d, int c) {
5859
ll mid = ans + i;
5960

6061
// Reset maximums and minimums
61-
vector<ll> half_plane(4);
62+
for (int j = 0; j < 4; j++) half_plane[j] = -INF;
6263
ll mx1 = -INF, mx2 = -INF, smx1 = -INF, smx2 = -INF;
6364

6465
for (int i = 0, j = n - 1; i < n; i++) {

0 commit comments

Comments
 (0)