結果
| 問題 |
No.1787 Do Use Dynamic Tree
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:07:14 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 6,812 bytes |
| コンパイル時間 | 1,206 ms |
| コンパイル使用メモリ | 78,728 KB |
| 実行使用メモリ | 23,532 KB |
| 最終ジャッジ日時 | 2025-05-14 13:09:01 |
| 合計ジャッジ時間 | 15,883 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 26 TLE * 1 -- * 11 |
ソースコード
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <vector>
// Define the maximum number of vertices based on problem constraints
const int MAXN = 200005;
// Adjacency list to represent the tree structure. adj[i] stores neighbors of vertex i.
std::vector<int> adj[MAXN];
// Array to store the values written on vertices. Use long long for safety, although N <= 2e5 fits in int.
long long values[MAXN];
int N; // Number of vertices in the tree
/**
* @brief Finds the best neighbor to move to from vertex u, avoiding vertex p.
*
* The "best" neighbor is defined by the following rules:
* 1. It must have the maximum possible value among all neighbors of u (excluding p).
* 2. If there is a tie (multiple neighbors have the same maximum value), the one with the largest vertex ID is chosen.
*
* @param u The current vertex.
* @param p The previous vertex in the path traversal (to avoid going back). Use 0 if u is the starting vertex.
* @return The vertex ID of the best neighbor. Returns -1 if no valid neighbor exists (e.g., u is a leaf in the traversal direction).
*/
inline int find_best_neighbor_simple(int u, int p) {
long long max_val = -1; // Initialize max value found so far to -1 (since vertex values are positive integers).
int best_neighbor_id = -1; // Initialize best neighbor ID found so far to -1.
// Iterate through all neighbors of vertex u
for (int v : adj[u]) {
// Skip the neighbor if it's the vertex p we just came from
if (v == p) continue;
long long current_val = values[v]; // Get the value of the neighbor vertex v
// Check if the current neighbor's value is greater than the maximum value found so far
if (current_val > max_val) {
max_val = current_val; // Update the maximum value
best_neighbor_id = v; // Update the best neighbor ID
}
// Check if the current neighbor's value is equal to the maximum value (a tie)
else if (current_val == max_val) {
// Apply the tie-breaking rule: choose the neighbor with the larger vertex ID.
// This comparison `v > best_neighbor_id` correctly handles the initial state where best_neighbor_id is -1,
// because any valid vertex ID v (>= 1) will be greater than -1.
if (v > best_neighbor_id) {
best_neighbor_id = v; // Update best_neighbor_id if v has a larger ID
}
}
}
// Return the ID of the best neighbor found according to the rules.
// If no valid neighbor was found (e.g., u is a leaf node when viewed from p), returns -1.
return best_neighbor_id;
}
/**
* @brief Solves a single query to find the vertex w that maximizes the sequence f(u, w) lexicographically.
*
* This function implements a greedy strategy: Starting from vertex u, it repeatedly moves to the "best" neighbor
* (determined by find_best_neighbor_simple) until it cannot move further. The vertex where this path ends is returned as the answer.
* The rationale is that maximizing the sequence lexicographically heavily prioritizes earlier elements, which aligns with
* greedily choosing the neighbor with the highest value at each step. The vertex ID tie-breaking ensures a unique path is chosen.
* The final node of this path is taken as the argmax_w f(u, w).
*
* @param u The starting vertex for the paths f(u, w).
* @return The vertex ID w that maximizes f(u, w) lexicographically based on the greedy strategy.
*/
inline int solve_query(int u) {
int current_node = u; // Start the path traversal from vertex u.
int prev_node = 0; // Use 0 as a placeholder for the parent of the starting node (no parent initially).
while (true) {
// Find the best neighbor to move to from the current node, ensuring we don't move back to the previous node.
int next_node = find_best_neighbor_simple(current_node, prev_node);
// If find_best_neighbor_simple returns -1, it signifies that there are no valid neighbors to continue the path.
// This occurs if current_node is a leaf in the direction away from prev_node.
if (next_node == -1) {
break; // Terminate the path construction process.
}
// Move to the determined best neighbor.
prev_node = current_node; // Update the previous node to the current node.
current_node = next_node; // Update the current node to the chosen next node.
}
// The loop terminates when the path cannot be extended further following the greedy rule.
// The 'current_node' at this point is the endpoint 'w' of the constructed path.
return current_node;
}
int main() {
// Set up fast input/output
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
// Read the number of vertices N
std::cin >> N;
// Read N-1 edges to build the tree structure using an adjacency list
for (int i = 0; i < N - 1; ++i) {
int u, v;
std::cin >> u >> v;
adj[u].push_back(v); // Add edge between u and v
adj[v].push_back(u); // Add edge between v and u (undirected graph)
}
// Initialize the values array: vertex i initially holds the value i
for (int i = 1; i <= N; ++i) {
values[i] = i;
}
// Read the number of queries Q
int Q;
std::cin >> Q;
// Variable to store the answer of the previous query, needed for query decryption
long long last_ans = 0;
// Process each of the Q queries
for (int i = 0; i < Q; ++i) {
long long u_raw, v_raw; // Read the raw (encrypted) query vertices
std::cin >> u_raw >> v_raw;
// Decrypt the query vertices using the provided formula and the answer from the previous query
// The formula is: result = (((raw_input + N - 1 + last_ans) % N) + 1)
long long u_true = ((u_raw + N - 1 + last_ans) % N) + 1;
long long v_true = ((v_raw + N - 1 + last_ans) % N) + 1;
// Perform the swap operation on the values at the decrypted vertices u_true and v_true.
// Check if u_true and v_true are different before swapping, as swapping a value with itself is pointless
// and std::swap typically requires distinct memory locations (though it might work correctly anyway).
if (u_true != v_true) {
std::swap(values[u_true], values[v_true]);
}
// Call the solve_query function with the decrypted starting vertex u_true
// to find the vertex w that maximizes f(u_true, w).
last_ans = solve_query(u_true);
// Output the result (the vertex ID w) for the current query, followed by a newline.
std::cout << last_ans << "\n";
}
return 0; // Indicate successful execution
}
qwewe