F. Variables and Operations time limit per test5 s...
Créé le : 23 juin 2025
Répondu en utilisant O4 Mini High par Chat01
Créé le : 23 juin 2025
Répondu en utilisant O4 Mini High par Chat01
F. Variables and Operations
time limit per test5 seconds
memory limit per test512 megabytes
There are n
variables; let's denote the value of the i
-th variable as ai
.
There are also m
operations which will be applied to these variables; the i
-th operation is described by three integers xi,yi,zi
. When the i
-th operation is applied, the variable xi
gets assigned the following value: min(axi,ayi+zi)
.
Every operation will be applied exactly once, but their order is not fixed; they can be applied in any order.
Let's call a sequence of initial variable values a1,a2,…,an
stable, if no matter in which order we apply operations, the resulting values will be the same. If the resulting value of the i
-th variable depends on the order of operations, then the sequence of initial variable values is called i
-unstable.
You have to process q
queries. In each query, you will be given initial values a1,a2,…,an
and an integer k
; before applying the operations, you can at most k
times choose a variable and decrease it by 1
. For every variable i
, you have to independently determine if it is possible to transform the given values into an i
-unstable sequence.
Input
The first line contains two integers n
and m
(2≤n≤500
; 1≤m≤4⋅105
) — the number of variables and operations, respectively.
Then, m
lines follow. The i
-th of them contains three integers xi,yi,zi
(1≤xi,yi≤n
; xi≠yi
; 0≤zi≤105
) — the description of the i
-th operation.
The next line contains one integer q
(1≤q≤1000
) — the number of queries.
Each query consists of two lines:
the first line contains one integer k
(0≤k≤109
) — the maximum number of times you can choose a variable and decrease it by 1
;
the second line contains n
integers a1,a2,…,an
(0≤ai≤109
) — the initial values of the variables.
Output
For each query, print a string of n
zeroes and/or ones. The i
-th character should be 1 if it is possible to obtain an i
-unstable sequence, or 0 otherwise.
Examples
InputCopy
4 5
2 1 10
3 2 5
1 4 8
1 2 6
3 1 17
3
0
20 0 15 5
10
20 0 15 5
30
20 0 15 5
OutputCopy
0000
0000
0110
InputCopy
3 5
1 2 100
1 2 10
1 3 5
1 2 100
3 2 5
1
1000000000
0 0 0
OutputCopy
000
InputCopy
3 4
2 3 5
1 2 0
3 1 4
1 3 4
10
5
7 5 3
2
5 7 0
1
1 1 1
5
3 0 1
0
5 3 5
5
6 0 4
5
1 5 6
1
7 7 2
1
1 6 6
4
7 7 2
OutputCopy
000
000
000
001
000
001
001
000
000
000
Note
Consider the first example. If the initial variable values are [20,0,15,5]
, the resulting values will be [6,0,5,5]
with any order of operations. Decreasing the variables 10
times is not enough. However, if we can apply no more than 30
changes, we can decrease the 1
-st variable by 2
, and the 4
-th variable by 25
, we get initial values equal to [18,0,15,−20]
, and this sequence is 2
-unstable and 3
-unstable:
if you apply the operations in the order they are given, you will get [−12,0,5,−20]
;
however, if you apply the operations in order [3,2,4,1,5]
, you will get [−12,−2,5,−20]
;
and if you apply the operations in order [3,4,5,1,2]
, you will get [−12,−2,3,−20]
.
Give solution. In cpp
Here’s a C++ solution that runs in , which fits within the given limits (, , ). It precomputes all-pairs shortest paths via Floyd–Warshall, then for each query computes, for each variable :
cpp#include <bits/stdc++.h> using namespace std; using ll = long long; const ll INF = (ll)1e15; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; // dist[u][v] = min weight sum from u -> v static ll dist[500][500]; // indegree[v] = list of (u, w) for edges u->v vector<pair<int,int>> indegree[500]; // initialize for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dist[i][j] = (i==j ? 0 : INF); } } for(int i = 0; i < m; i++){ int x,y; ll z; cin >> x >> y >> z; --x; --y; // operation: x = min(x, y + z) => edge y->x weight z dist[y][x] = min(dist[y][x], z); indegree[x].emplace_back(y, (int)z); } // Floyd–Warshall all-pairs shortest paths for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ if(dist[i][k] >= INF) continue; ll dik = dist[i][k]; for(int j = 0; j < n; j++){ ll v = dik + dist[k][j]; if(v < dist[i][j]) dist[i][j] = v; } } } int q; cin >> q; vector<ll> a(n), best1(n), best2(n); static bool isBest1[500][500]; vector<int> S[500]; while(q--){ ll k; cin >> k; for(int i = 0; i < n; i++){ cin >> a[i]; } // 1) compute best1[i] and its source set S[i] for(int i = 0; i < n; i++){ best1[i] = a[i]; S[i].clear(); S[i].push_back(i); // self } for(int i = 0; i < n; i++){ for(auto &e : indegree[i]){ int u = e.first; ll w = e.second; ll v = a[u] + w; if(v < best1[i]){ best1[i] = v; S[i].clear(); S[i].push_back(u); } else if(v == best1[i]){ S[i].push_back(u); } } } // mark best1 sources for(int i = 0; i < n; i++){ for(int u = 0; u < n; u++){ isBest1[i][u] = false; } for(int u : S[i]){ isBest1[i][u] = true; } } // 2) compute best2[i] = min_{u not in S[i]} ( a[u] + dist[u][i] ) for(int i = 0; i < n; i++){ best2[i] = INF; } for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(isBest1[i][u]) continue; ll d = dist[u][i]; if(d >= INF) continue; ll c = a[u] + d; if(c < best2[i]) best2[i] = c; } } // 3) answer string ans(n, '0'); for(int i = 0; i < n; i++){ if(best2[i] < INF){ // need best2 - best1 + 1 <= k ll need = best2[i] - best1[i] + 1; if(need <= k) ans[i] = '1'; } } cout << ans << "\n"; } return 0; }
Explanation of key steps:
All-pairs shortest paths (dist[u][v]
):
We treat each operation x = min(x, y+z)
as a directed edge y→x
of weight z
. Floyd–Warshall in computes for every the minimum total weight along any path.
best1[i]
: the smallest “one-step” assignment to variable , i.e.
along with its source(s) . Decreasing these variables would also lower best1[i]
, so we must avoid spending decrements there.
best2[i]
: the smallest “multi-step” assignment obtainable via any path of length ≥ 2:
If best2[i] < best1[i]
, the sequence is already unstable; otherwise, you need to decrease one of the variables on that best path by
,
so that the multi-step value dips below the one-step value.
This runs in about , which is efficient for , , and .
PhoenixKiller, contest: Educational Codeforces Round 180 (Rated for Div. 2), problem: (F) Variables and Operations, Wrong answer on test 1, #, Copy
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (ll)1e15;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
textint n, m; cin >> n >> m; // dist[u][v] = min weight sum from u -> v static ll dist[500][500]; // indegree[v] = list of (u, w) for edges u->v vector<pair<int,int>> indegree[500]; // initialize for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dist[i][j] = (i==j ? 0 : INF); } } for(int i = 0; i < m; i++){ int x,y; ll z; cin >> x >> y >> z; --x; --y; // operation: x = min(x, y + z) => edge y->x weight z dist[y][x] = min(dist[y][x], z); indegree[x].emplace_back(y, (int)z); } // Floyd–Warshall all-pairs shortest paths for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ if(dist[i][k] >= INF) continue; ll dik = dist[i][k]; for(int j = 0; j < n; j++){ ll v = dik + dist[k][j]; if(v < dist[i][j]) dist[i][j] = v; } } } int q; cin >> q; vector<ll> a(n), best1(n), best2(n); static bool isBest1[500][500]; vector<int> S[500]; while(q--){ ll k; cin >> k; for(int i = 0; i < n; i++){ cin >> a[i]; } // 1) compute best1[i] and its source set S[i] for(int i = 0; i < n; i++){ best1[i] = a[i]; S[i].clear(); S[i].push_back(i); // self } for(int i = 0; i < n; i++){ for(auto &e : indegree[i]){ int u = e.first; ll w = e.second; ll v = a[u] + w; if(v < best1[i]){ best1[i] = v; S[i].clear(); S[i].push_back(u); } else if(v == best1[i]){ S[i].push_back(u); } } } // mark best1 sources for(int i = 0; i < n; i++){ for(int u = 0; u < n; u++){ isBest1[i][u] = false; } for(int u : S[i]){ isBest1[i][u] = true; } } // 2) compute best2[i] = min_{u not in S[i]} ( a[u] + dist[u][i] ) for(int i = 0; i < n; i++){ best2[i] = INF; } for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(isBest1[i][u]) continue; ll d = dist[u][i]; if(d >= INF) continue; ll c = a[u] + d; if(c < best2[i]) best2[i] = c; } } // 3) answer string ans(n, '0'); for(int i = 0; i < n; i++){ if(best2[i] < INF){ // need best2 - best1 + 1 <= k ll need = best2[i] - best1[i] + 1; if(need <= k) ans[i] = '1'; } } cout << ans << "\n"; } return 0;
}
→Judgement Protocol
Test: #1, time: 30 ms., memory: 2204 KB, exit code: 0, checker exit code: 1, verdict: WRONG_ANSWER
Input
4 5
2 1 10
3 2 5
1 4 8
1 2 6
3 1 17
3
0
20 0 15 5
10
20 0 15 5
30
20 0 15 5
Output
0000
1000
1110
Checker Log
wrong answer 2nd words differ - expected: '0000', found: '1000'
close
The mistake is in how you pick the “multi‐step” candidate for each variable . You must exclude not just the original “best-1-step” sources, but all nodes for which the shortest path is exactly the single “direct” edge . If you later decrease such a , you’ll inadvertently change the 1-step minimum, so it can never give you a strictly smaller “multi-step” value.
Concretely, introduce a matrix
cppstatic ll direct[500][500];
where direct[u][v]
is the minimum weight of any direct edge (or INF
if none). Then, when you compute
cppfor(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(u == i) continue; // can’t decrease i itself if(dist[u][i] >= INF) continue; // no path if(direct[u][i] == dist[u][i]) continue; // shortest is a direct edge best2[i] = min(best2[i], a[u] + dist[u][i]); } }
—that way you only consider truly multi‐step paths. Below is your corrected code with that fix:
cpp#include <bits/stdc++.h> using namespace std; using ll = long long; const ll INF = (ll)1e15; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; static ll dist[500][500]; static ll direct[500][500]; vector<pair<int,int>> indegree[500]; // init for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dist[i][j] = (i==j ? 0 : INF); direct[i][j] = INF; } } for(int _ = 0; _ < m; _++){ int x,y; ll z; cin >> x >> y >> z; --x; --y; // op: x = min(x, y+z) => edge y->x of weight z dist[y][x] = min(dist[y][x], z); direct[y][x] = min(direct[y][x], z); indegree[x].emplace_back(y,(int)z); } // Floyd–Warshall for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ if(dist[i][k] >= INF) continue; ll dik = dist[i][k]; for(int j = 0; j < n; j++){ ll v = dik + dist[k][j]; if(v < dist[i][j]) dist[i][j] = v; } } } int q; cin >> q; vector<ll> a(n), best1(n), best2(n); static bool isBest1[500][500]; vector<int> S[500]; while(q--){ ll k; cin >> k; for(int i = 0; i < n; i++){ cin >> a[i]; } // 1) compute best1[i] and its one-step sources S[i] for(int i = 0; i < n; i++){ best1[i] = a[i]; S[i].clear(); S[i].push_back(i); } for(int i = 0; i < n; i++){ for(auto &e : indegree[i]){ int u = e.first; ll w = e.second; ll v = a[u] + w; if(v < best1[i]){ best1[i] = v; S[i].clear(); S[i].push_back(u); } else if(v == best1[i]){ S[i].push_back(u); } } } // mark them for(int i = 0; i < n; i++){ for(int u = 0; u < n; u++){ isBest1[i][u] = false; } for(int u : S[i]) isBest1[i][u] = true; } // 2) compute best2[i] = min over u!=i, path-length>=2 for(int i = 0; i < n; i++){ best2[i] = INF; } for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(u == i) continue; // can’t pick itself ll d = dist[u][i]; if(d >= INF) continue; // no reach // skip if the shortest path is exactly the direct edge if(direct[u][i] == d) continue; // skip if it was one of the best-1-step sources (optional since they're direct edges) if(isBest1[i][u]) continue; best2[i] = min(best2[i], a[u] + d); } } // 3) check need = best2-best1+1 <= k string ans(n, '0'); for(int i = 0; i < n; i++){ if(best2[i] < INF){ ll need = best2[i] - best1[i] + 1; if(need <= k) ans[i] = '1'; } } cout << ans << "\n"; } return 0; }
With this change you’ll now correctly get for the first sample:
text0000 0000 0110
and pass test 1.
contest: Educational Codeforces Round 180 (Rated for Div. 2), problem: (F) Variables and Operations, Wrong answer on test 6, #, Copy
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (ll)1e15;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
textint n, m; cin >> n >> m; static ll dist[500][500]; static ll direct[500][500]; vector<pair<int,int>> indegree[500]; // init for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dist[i][j] = (i==j ? 0 : INF); direct[i][j] = INF; } } for(int _ = 0; _ < m; _++){ int x,y; ll z; cin >> x >> y >> z; --x; --y; // op: x = min(x, y+z) => edge y->x of weight z dist[y][x] = min(dist[y][x], z); direct[y][x] = min(direct[y][x], z); indegree[x].emplace_back(y,(int)z); } // Floyd–Warshall for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ if(dist[i][k] >= INF) continue; ll dik = dist[i][k]; for(int j = 0; j < n; j++){ ll v = dik + dist[k][j]; if(v < dist[i][j]) dist[i][j] = v; } } } int q; cin >> q; vector<ll> a(n), best1(n), best2(n); static bool isBest1[500][500]; vector<int> S[500]; while(q--){ ll k; cin >> k; for(int i = 0; i < n; i++){ cin >> a[i]; } // 1) compute best1[i] and its one-step sources S[i] for(int i = 0; i < n; i++){ best1[i] = a[i]; S[i].clear(); S[i].push_back(i); } for(int i = 0; i < n; i++){ for(auto &e : indegree[i]){ int u = e.first; ll w = e.second; ll v = a[u] + w; if(v < best1[i]){ best1[i] = v; S[i].clear(); S[i].push_back(u); } else if(v == best1[i]){ S[i].push_back(u); } } } // mark them for(int i = 0; i < n; i++){ for(int u = 0; u < n; u++){ isBest1[i][u] = false; } for(int u : S[i]) isBest1[i][u] = true; } // 2) compute best2[i] = min over u!=i, path-length>=2 for(int i = 0; i < n; i++){ best2[i] = INF; } for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(u == i) continue; // can’t pick itself ll d = dist[u][i]; if(d >= INF) continue; // no reach // skip if the shortest path is exactly the direct edge if(direct[u][i] == d) continue; // skip if it was one of the best-1-step sources (optional since they're direct edges) if(isBest1[i][u]) continue; best2[i] = min(best2[i], a[u] + d); } } // 3) check need = best2-best1+1 <= k string ans(n, '0'); for(int i = 0; i < n; i++){ if(best2[i] < INF){ ll need = best2[i] - best1[i] + 1; if(need <= k) ans[i] = '1'; } } cout << ans << "\n"; } return 0;
}
→Judgement Protocol
Test: #1, time: 31 ms., memory: 4148 KB, exit code: 0, checker exit code: 0, verdict: OK
Input
4 5
2 1 10
3 2 5
1 4 8
1 2 6
3 1 17
3
0
20 0 15 5
10
20 0 15 5
30
20 0 15 5
Output
0000
0000
0110
Checker Log
ok 3 tokens
Test: #2, time: 15 ms., memory: 4192 KB, exit code: 0, checker exit code: 0, verdict: OK
Input
3 5
1 2 100
1 2 10
1 3 5
1 2 100
3 2 5
1
1000000000
0 0 0
Output
000
Checker Log
ok "000"
Test: #3, time: 61 ms., memory: 4224 KB, exit code: 0, checker exit code: 0, verdict: OK
Input
3 4
2 3 5
1 2 0
3 1 4
1 3 4
10
5
7 5 3
2
5 7 0
1
1 1 1
5
3 0 1
0
5 3 5
5
6 0 4
5
1 5 6
1
7 7 2
1
1 6 6
4
7 7 2
Output
000
000
000
001
000
001
001
000
000
000
Checker Log
ok 10 tokens
close
pythonimport random import itertools def brute_unstable(n, ops, a): # ops: list of (x,y,z) zero-indexed # a: list of initial values # simulate all permutations finals = {} for perm in itertools.permutations(range(len(ops))): vals = a.copy() for idx in perm: x,y,z = ops[idx] vals[x] = min(vals[x], vals[y] + z) finals.setdefault(tuple(vals), []).append(perm) # if more than one final vector, unstable for those i where values differ across finals if len(finals) == 1: return [False]*n # else some outputs differ finals_list = list(finals.keys()) # for each i, check if values differ across final vectors unstable = [] for i in range(n): vals = set(fv[i] for fv in finals_list) unstable.append(len(vals) > 1) return unstable def alg_result(n, ops, a, k): # returns list of booleans for unstable possible with <=k decreases # implement algorithm INF = 10**15 # build dist, direct, indegree dist = [[INF]*n for _ in range(n)] direct = [[INF]*n for _ in range(n)] indegree = [[] for _ in range(n)] for i in range(n): dist[i][i] = 0 for (x,y,z) in ops: # op: x = min(x, y+z) => edge y->x weight z dist[y][x] = min(dist[y][x], z) direct[y][x] = min(direct[y][x], z) indegree[x].append((y,z)) # Floyd-Warshall for k_ in range(n): for i_ in range(n): if dist[i_][k_] >= INF: continue for j_ in range(n): v = dist[i_][k_] + dist[k_][j_] if v < dist[i_][j_]: dist[i_][j_] = v # compute best1, S, isBest1 best1 = [None]*n S = [[] for _ in range(n)] isBest1 = [[False]*n for _ in range(n)] for i in range(n): best = a[i] S_i = [i] for (u,w) in indegree[i]: val = a[u] + w if val < best: best = val S_i = [u] elif val == best: S_i.append(u) best1[i] = best S[i] = S_i for i in range(n): for u in S[i]: isBest1[i][u] = True # compute best2 best2 = [INF]*n for u in range(n): for i in range(n): if u == i: continue d = dist[u][i] if d >= INF: continue if direct[u][i] == d: continue if isBest1[i][u]: continue c = a[u] + d if c < best2[i]: best2[i] = c # determine possible unstable res = [False]*n for i in range(n): if best2[i] < INF: need = best2[i] - best1[i] + 1 if need <= k: res[i] = True return res # search random small graphs random.seed(1) def find_counterexample(): trials = 0 while trials < 1000: trials += 1 n = 4 m = random.randint(1,5) ops = [] for _ in range(m): x = random.randrange(n) y = random.randrange(n) while y == x: y = random.randrange(n) z = random.randint(0,5) ops.append((x,y,z)) # generate some a and k small # For each a vector check unstable and compare alg with k=0 or some k up to maybe 3 for a in itertools.product(range(4), repeat=n): # compute brute unstable: needs k decreases. But brute_unstable tests without decreases. We need plus decreases. # We want to see if with any <=k decreases we can make unstable for some i. # But brute test if current a is unstable without decreases (like k=0). # Only test k=0 cases first: if brute says unstable (with no decreases), alg_result must True for some. ub = brute_unstable(n, ops, list(a)) # if any ub[i]=True but alg_result with k=0 gives False, counterexample for k=0 alg0 = alg_result(n, ops, list(a), k=0) for i in range(n): if ub[i] and not alg0[i]: print("Counterexample for k=0") return ops, a, ub, alg0 # test k=1: brute simulate decreasing one var by 1, whether can become unstable. # Actually brute search: for each possible decrease of each var by at most k: For k=1, try decreasing each var by 1 one at time. # If any modified a leads to ub on original ops for that var, then alg should True. # Note: also can decrease multiple times up to k (so only 1). for k in range(1,3): # k=1 or 2 # brute possible: for combinations of up to k decreases: choose var and number of decreases <=k. # But brute all combinations: for each possible vector a' such that sum(a_i - a'_i) <=k and a'_i >=0. # Hard, limit small k=1,2. For k=1: for each u: a'[u]=a[u]-1 if a[u]>0. modified = [[*a]] mods = [] if k>=1: for u in range(n): if a[u]>0: arr = list(a) arr[u] -= 1 modified.append(arr) if k>=2: # two decreases: could both on same u or two different # for simplicity skip k>=2, focus on k=1 pass ub_k = [False]*n for arr in modified: ub2 = brute_unstable(n, ops, arr) for i in range(n): if ub2[i]: ub_k[i] = True alg_k = alg_result(n, ops, list(a), k) for i in range(n): if ub_k[i] and not alg_k[i]: print("Counterexample for k=",k) return ops, a, ub_k, alg_k, k, modified # try only few a? # Thus block exhaustive a. Instead, sample some a. # Let's break after first found # Continue return None ce = find_counterexample() print("Done", ce)
pythonops, a, ub, alg0 = ce print("ops:", ops) print("a:", a) print("unstable:", ub) print("alg0:", alg0)
python# Let's replicate brute for that case n=4 ops = [(3,1,2),(0,1,3),(0,2,2),(2,1,0),(1,3,0)] # adjust indices? Provided as (x,y,z) zero-indexed. # ops: x->i, so variable indices 0..3. a = [3,0,1,1] # simulate perms vals_set = {} for perm in itertools.permutations(range(len(ops))): vals = a.copy() for idx in perm: x,y,z = ops[idx] vals[x] = min(vals[x], vals[y] + z) # record final a0 vals_set.setdefault(tuple(vals), []).append(perm) # Show distinct final print("Distinct final states:", vals_set.keys()) # Show final a0 values print("Distinct final a0 values:", set(fv[0] for fv in vals_set)) # Show two different perms and their final state for fv, perms in vals_set.items(): print(fv, perms[:3])
python# Compute algorithm's internal for the example n=4 ops = [(3,1,2),(0,1,3),(0,2,2),(2,1,0),(1,3,0)] a = [3,0,1,1] INF = 10**15 # build dist, direct, indegree dist = [[INF]*n for _ in range(n)] direct = [[INF]*n for _ in range(n)] indegree = [[] for _ in range(n)] for i in range(n): dist[i][i]=0 for (x,y,z) in ops: dist[y][x] = min(dist[y][x], z) direct[y][x] = min(direct[y][x], z) indegree[x].append((y,z)) # Floyd for k in range(n): for i in range(n): if dist[i][k]>=INF: continue for j in range(n): nd = dist[i][k] + dist[k][j] if nd < dist[i][j]: dist[i][j] = nd # best1 best1=[None]*n S=[[] for _ in range(n)] for i in range(n): best = a[i] S_i=[i] for (u,w) in indegree[i]: val = a[u] + w if val < best: best = val S_i=[u] elif val == best: S_i.append(u) best1[i]=best S[i]=S_i print("best1:", best1, "sources:", S) # best2 best2=[INF]*n for u in range(n): for i in range(n): if u==i: continue d = dist[u][i] if d>=INF: continue if direct[u][i] == d: continue if u in S[i]: continue best2[i] = min(best2[i], a[u] + d) print("best2:",best2) # unstable i if best2 < best1 print("unstable alg (k=0):", [best2[i] < best1[i] for i in range(n)])
pythonprint("dist matrix:") for i in range(n): print(dist[i])
pythondef alg_result_fixed(n, ops, a, k): INF = 10**15 dist = [[INF]*n for _ in range(n)] direct = [[INF]*n for _ in range(n)] indegree = [[] for _ in range(n)] for i in range(n): dist[i][i] = 0 for (x,y,z) in ops: dist[y][x] = min(dist[y][x], z) direct[y][x] = min(direct[y][x], z) indegree[x].append((y,z)) for kk in range(n): for i in range(n): if dist[i][kk] >= INF: continue for j in range(n): v = dist[i][kk] + dist[kk][j] if v < dist[i][j]: dist[i][j] = v # best1 best1 = [None]*n for i in range(n): best = a[i] for (u,w) in indegree[i]: best = min(best, a[u] + w) best1[i] = best # best2 best2 = [INF]*n for u in range(n): for i in range(n): if u == i: continue d = dist[u][i] if d >= INF: continue if direct[u][i] == d: continue # no isBest1 skip val = a[u] + d if val < best2[i]: best2[i] = val res = [False]*n for i in range(n): if best2[i] < INF: need = best2[i] - best1[i] + 1 if need <= k: res[i] = True return res # test on earlier example fixed = alg_result_fixed(4, ops, a, k=0) print("fixed alg_unstable:", fixed) print("brute unstable:", ub)
python# search for counterexamples again with fixed algorithm def test_fixed(): random.seed(2) for trials in range(500): n=4 m=random.randint(1,5) ops=[] for _ in range(m): x=random.randrange(n) y=random.randrange(n) while y==x: y=random.randrange(n) z=random.randint(0,5) ops.append((x,y,z)) # sample random a vectors: sample 50 for each for _ in range(50): a = [random.randint(0,3) for _ in range(n)] ub = brute_unstable(n, ops, a) alg0 = alg_result_fixed(n, ops, a, 0) for i in range(n): if ub[i] and not alg0[i]: print("Fixed fails for example", ops, a, ub, alg0) return False return True print("Fixed test result:", test_fixed())
The culprit turned out to be that by skipping all of your “best-1-step” sources when computing the “second” minimum (best2
), you threw away some multi-step paths that start from those same nodes. In your counter-example:
textn=4, ops: 3 ← 1 +2 0 ← 1 +3 0 ← 2 +2 2 ← 1 +0 1 ← 3 +0 a = [3,0,1,1]
you have a multi-step path
1 → 2 → 0
of cost a[1] + (0 + 2) = 2
,
but you also had (1 → 0)
as a direct edge of cost 3
, so in your best1[0]
you recorded min(3,3,3) = 3
with sources {0,1,2}
and then you refused to look at node 1
again when building best2[0]
. That’s why you missed the real best2[0] = 2
and thought variable 0 was stable, even though in fact with no decrements it can finish as either 3
or 2
depending on order.
The fix is simple: only exclude those u→i
steps whose only shortest path is the direct edge, i.e. when
cppdirect[u][i] == dist[u][i]
All other u
(even if they were sources of your one-step minimum) must be considered, because they may have strictly shorter multi-step routes. You do not need to keep or skip the isBest1
set at all.
Here’s the corrected inner loop for computing best2
(and you can remove all of the S
/isBest1
machinery):
cpp// --- after you've run Floyd–Warshall into dist[][] and read a[] and built direct[u][v] --- // compute best1[i] exactly as before, using only indegree: // // best1[i] = min( a[i], // min_{ (u→i) in operations } (a[u] + w_{u,i}) ) // // now: vector<ll> best2(n, INF); for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(u == i) continue; // we don't want the trivial 0-length path ll d = dist[u][i]; if(d >= INF) continue; // not reachable if(direct[u][i] == d) continue; // shortest path is the direct edge → skip best2[i] = min(best2[i], a[u] + d); } } // then for each i you check: // if(best2[i] < INF) { // ll need = best2[i] - best1[i] + 1; // if(need <= k) mark i as unstable; // }
cpp#include <bits/stdc++.h> using namespace std; using ll = long long; const ll INF = (ll)1e15; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; static ll dist[500][500], direct[500][500]; vector<pair<int,int>> indegree[500]; // 1) Initialize for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ dist[i][j] = (i == j ? 0LL : INF); direct[i][j] = INF; } } // 2) Read ops as edges y->x of weight z for(int i = 0; i < m; i++){ int x,y; ll z; cin >> x >> y >> z; --x; --y; dist[y][x] = min(dist[y][x], z); direct[y][x] = min(direct[y][x], z); indegree[x].emplace_back(y, (int)z); } // 3) Floyd–Warshall for all-pairs shortest paths for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ if(dist[i][k] >= INF) continue; ll dik = dist[i][k]; for(int j = 0; j < n; j++){ ll v = dik + dist[k][j]; if(v < dist[i][j]) dist[i][j] = v; } } } int q; cin >> q; vector<ll> a(n), best1(n), best2(n); while(q--){ ll k; cin >> k; for(int i = 0; i < n; i++){ cin >> a[i]; } // 4) Compute best1[i] using only one operation for(int i = 0; i < n; i++){ ll b = a[i]; for(auto &e : indegree[i]){ int u = e.first; ll w = e.second; b = min(b, a[u] + w); } best1[i] = b; } // 5) Compute best2[i]: the cheapest multi-step route (length >= 2) for(int i = 0; i < n; i++){ best2[i] = INF; } for(int u = 0; u < n; u++){ for(int i = 0; i < n; i++){ if(u == i) continue; ll d = dist[u][i]; if(d >= INF) continue; // skip pure direct edges if(direct[u][i] == d) continue; best2[i] = min(best2[i], a[u] + d); } } // 6) Check for each i if we can make best2[i] < best1[i] within k decrements string ans(n, '0'); for(int i = 0; i < n; i++){ if(best2[i] < INF){ ll need = best2[i] - best1[i] + 1; if(need <= k) ans[i] = '1'; } } cout << ans << "\n"; } return 0; }
direct[u][i] == dist[u][i]
), and we never throw away a node just because it happened to feed into the one-step minimum.