C - ネットワークの通信コスト / Network Communication Cost 解説 by admin
Gemini 3.0 Flash (Thinking)Overview
In a tree structure consisting of \(N\) vertices, we need to handle toggling vertex “modes (normal/inverted),” updating a correction parameter \(S\), and computing “the sum of communication costs along the path between two vertices + \(S \times\) number of vertices.” Since the constraints are relatively small at \(N, Q \leq 5000\), this can be solved by performing a tree traversal for each query.
Analysis
1. Effective Coordinates and Communication Cost
The effective coordinates \((X'_i, Y'_i)\) of each relay station \(i\) change depending on the mode as follows: - Normal mode: \((X_i, Y_i)\) - Inverted mode: \((-X_i, -Y_i)\)
The communication cost between adjacent vertices \(u, v\) is the Manhattan distance \(|X'_u - X'_v| + |Y'_u - Y'_v|\). Note that when a mode is toggled, the costs of all links connected to that relay station change.
2. Query Processing and Constraint Evaluation
The constraints of this problem are \(N, Q \leq 5000\). If \(N, Q\) were on the order of \(10^5\), advanced data structures such as LCA (Lowest Common Ancestor), prefix sums on trees, or Heavy-Light Decomposition would be necessary. However, with the given constraints, an overall time complexity of around \(O(NQ)\) fits within the time limit (typically 2 seconds).
Therefore, each time a type 3 query (path computation) arrives, it is sufficient to identify the path from \(A\) to \(B\) using Breadth-First Search (BFS) or Depth-First Search (DFS) and compute the cost directly.
3. Organizing the Formula
The value to compute for a type 3 query is as follows: $\(\sum_{(u, v) \in \text{Path}} (\text{Manhattan distance}) + S \times (\text{number of vertices on the path})\)\( If the number of vertices on the path is \)K\(, then the number of edges is \)K-1$.
Algorithm
- Preparation:
- Store the graph in adjacency list format.
- Manage the current mode of each vertex (\(1\) or \(-1\)) using an array.
- Query Processing:
- Type 1 (
1 C): Invert the value ofmode[C](e.g.,mode[C] *= -1). - Type 2 (
2 W): Add \(W\) to the variable \(S\). - Type 3 (
3 A B):- Perform BFS starting from \(A\), recording the “parent” of each vertex while searching for \(B\).
- Trace back from \(B\) to \(A\) by following parent pointers to identify the path.
- While traversing the path, compute the effective coordinates of adjacent vertices and sum up the Manhattan distances.
- Simultaneously count the number of vertices \(K\) on the path.
- Output
(total distance) + S * K.
- Type 1 (
Complexity
- Time Complexity: \(O(Q \times N)\)
- For each query, in the worst case, a traversal of the entire tree (\(O(N)\)) is performed, resulting in an overall complexity of \(O(QN)\).
- This amounts to approximately \(5000 \times 5000 = 2.5 \times 10^7\) operations, which comfortably fits within the time limit.
- Space Complexity: \(O(N)\)
- The memory required for the adjacency list, coordinate data, mode management, and traversal arrays is proportional to \(N\).
Implementation Notes
Overflow prevention: Coordinates, the value of \(S\), and the final answer can become large, so use the
long longtype in C++.Mode management: By representing normal mode as \(1\) and inverted mode as \(-1\) numerically, effective coordinates can be computed concisely as
X[i] * mode[i].Path reconstruction: By recording the predecessor as
parent[v] = uduring BFS, the path can be traced in reverse from destination \(B\) back to \(A\).Source Code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 座標やコストの計算においてオーバーフローを防ぐために long long を使用します
typedef long long ll;
// 制約に基づく中継局の最大数
const int MAXN = 5005;
// 中継局の情報を格納するためのグローバル配列
ll X[MAXN], Y[MAXN];
int mode[MAXN];
vector<int> adj[MAXN];
int parent[MAXN];
int q[MAXN];
int main() {
// 標準入出力の高速化
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q;
// N (中継局の数) と Q (操作の数) を読み込む
if (!(cin >> N >> Q)) return 0;
// 各中継局の基準座標を読み込む
for (int i = 1; i <= N; ++i) {
cin >> X[i] >> Y[i];
// 初期状態ではすべての中継局が通常モード (1)
mode[i] = 1;
}
// 木構造を形成する N-1 本の回線(エッジ)を読み込む
for (int i = 0; i < N - 1; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
// 補正パラメータ S の初期値は 0
ll S = 0;
// Q 個の操作を順に処理する
for (int k = 0; k < Q; ++k) {
int type;
cin >> type;
if (type == 1) {
// 操作 1: 中継局 C の動作モードを切り替える
int C;
cin >> C;
mode[C] *= -1; // 1 (通常) と -1 (反転) を切り替え
} else if (type == 2) {
// 操作 2: S に W を加算する
ll W;
cin >> W;
S += W;
} else if (type == 3) {
// 操作 3: A から B までのパス上の通信コストの総和 + S * (中継局の個数) を計算
int A, B;
cin >> A >> B;
// 木上の唯一の単純パスを見つけるために BFS (幅優先探索) を使用
// 各クエリごとに探索を行うため、parent 配列を初期化する
for (int i = 1; i <= N; ++i) parent[i] = 0;
int head = 0, tail = 0;
q[tail++] = A;
parent[A] = -1; // 開始ノードを示す特別な値
while (head < tail) {
int u = q[head++];
if (u == B) break; // 目的地 B に到達したら探索終了
for (int v : adj[u]) {
if (parent[v] == 0) {
parent[v] = u;
q[tail++] = v;
}
}
}
// B から A へと親を辿り、パスを再構成しながらコストを計算する
ll total_cost = 0;
ll station_count = 0;
int curr = B;
while (curr != -1) {
station_count++;
int nxt = parent[curr];
if (nxt != -1) {
// 現在のノード curr とその親 nxt の実効座標を計算
ll xu = X[curr] * mode[curr];
ll yu = Y[curr] * mode[curr];
ll xv = X[nxt] * mode[nxt];
ll yv = Y[nxt] * mode[nxt];
// 実効座標間のマンハッタン距離を計算して加算
ll dx = xu - xv;
if (dx < 0) dx = -dx;
ll dy = yu - yv;
if (dy < 0) dy = -dy;
total_cost += dx + dy;
}
curr = nxt;
}
// 最終的な計算結果を出力: (回線の通信コスト総和) + S * (パス上の中継局数)
total_cost += S * station_count;
cout << total_cost << "\n";
}
}
return 0;
}
This editorial was generated by gemini-3-flash-thinking.
投稿日時:
最終更新: