D - 都市ネットワーク / City Network Editorial by admin Editorial for printing
GPT 5.2 HighOverview
For each city, we find the “set of cities reachable within patience \(S_i\)”, then use the number of complaints \(C_i\) and the “mutual no-complaint” condition to count the number of friendly pairs.
Analysis
Key Observations
- The condition for representative \(i\) to have no complaint about city \(j\) is \(d(i,j)\le S_i\).
In other words, what matters for representative \(i\) is not the shortest distance itself, but whether the city is reachable within \(S_i\). - The number of complaints is $\( C_i = |\{j\ne i \mid d(i,j) > S_i\}| \)\( but this can be rephrased as follows: letting \)cnt_i\( be the number of cities reachable within \)S_i\( (including oneself), \)\( C_i = N - cnt_i \)\( Since \)d(i,i)=0\le S_i$ always holds, there is no off-by-one issue here.
Why the Naive Approach is Difficult
- All-pairs shortest paths (e.g., Floyd-Warshall) takes \(O(N^3)\), which for \(N=400\) amounts to roughly \(64,000,000\) updates and tends to be slow in Python depending on implementation.
- Furthermore, for friendly pair determination, we need checks like “\(d(i,j)\le S_i\)” where the threshold differs for each vertex, so it is natural to construct the “reachable set within \(S_i\)” for each \(i\).
Solution Strategy
- Since edge weights are positive, we run Dijkstra’s algorithm from each city \(i\).
- However, since we don’t need shortest distances exceeding \(S_i\), we cut off the search at \(S_i\) (bounded Dijkstra) to reduce unnecessary work.
- By storing the “set of cities reachable within \(S_i\)” as a bit set (using Python’s
intas a bitmask), pair checking becomes fast.
Algorithm
- Build the graph as an adjacency list (undirected).
- For each city \(i\), do the following:
- Set the limit as
limit = S[i]. - Run Dijkstra’s algorithm, but do not explore vertices whose distance exceeds
limit.- Initialize
distvalues toINF = limit + 1, so thatdist[v] != INFmeans “reachable within \(S_i\)”. - When the distance
dextracted from the priority queue exceedslimit, all subsequent distances will only be larger, so we can terminate the search (break).
- Initialize
- Store the set of reachable vertices as a bitmask
mask[i](set the bit to 1 if reachable). - Count the number of reachable vertices as
cnt(including oneself), and compute \(C_i = N - cnt\).
- Set the limit as
- For all pairs \((i,j)\) (\(i<j\)), count those satisfying:
- \(C_i = C_j\)
- and \(j\) is in
mask[i](\(d(i,j)\le S_i\)) - and \(i\) is in
mask[j](\(d(i,j)\le S_j\)) - If all conditions hold, add it as a friendly pair.
Bitmask checking can be done in \(O(1)\), for example, checking “\(j\) is in the set” with ((mask[i] >> j) & 1).
Complexity
- Time complexity: \(O\!\left(\sum_{i=1}^{N} (M_i \log N)\right) + O(N^2)\)
Here \(M_i\) is the number of edges actually considered for relaxation during the search bounded by distance \(S_i\), which can be small due to the bounded search. A rough worst-case estimate is \(O(NM\log N)\). - Space complexity: \(O(N+M)\) (graph) \(+\,O(N)\) (per-run
dist, masks/arrays, etc. Each mask is a singleintper vertex)
Implementation Notes
Bounded Dijkstra: Relax only when
nd <= limit, andbreakwhend > limitis extracted from the queue for speedup.Setting
INF = limit + 1allows checking “reachable within \(S_i\)” with the single conditiondist[v] != INF.Since \(N\le 400\), it is simple and efficient to use Python’s
intas a bit set (just OR with1<<v).The complaint count is computed as \(C_i = N - cnt\) from the reachable count
cnt(including oneself), which avoids overthinking the “\(j\ne i\)” in the definition (since oneself is always reachable, it stays consistent).Source Code
import sys
import heapq
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
M = next(it)
S = [next(it) for _ in range(N)]
g = [[] for _ in range(N)]
for _ in range(M):
u = next(it) - 1
v = next(it) - 1
w = next(it)
g[u].append((v, w))
g[v].append((u, w))
heappush = heapq.heappush
heappop = heapq.heappop
masks = [0] * N
C = [0] * N
for i in range(N):
limit = S[i]
INF = limit + 1
dist = [INF] * N
dist[i] = 0
hq = [(0, i)]
while hq:
d, u = heappop(hq)
if d != dist[u]:
continue
if d > limit:
break
for v, w in g[u]:
nd = d + w
if nd <= limit and nd < dist[v]:
dist[v] = nd
heappush(hq, (nd, v))
mask = 0
cnt = 0
for v, dv in enumerate(dist):
if dv != INF:
mask |= 1 << v
cnt += 1
masks[i] = mask
C[i] = N - cnt
ans = 0
for i in range(N):
mi = masks[i]
Ci = C[i]
for j in range(i + 1, N):
if Ci == C[j] and ((mi >> j) & 1) and ((masks[j] >> i) & 1):
ans += 1
print(ans)
if __name__ == "__main__":
main()
This editorial was generated by gpt-5.2-high.
posted:
last update: