結果
問題 |
No.1326 ふたりのDominator
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:13:48 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
WA
|
実行時間 | - |
コード長 | 13,625 bytes |
コンパイル時間 | 703 ms |
コンパイル使用メモリ | 75,628 KB |
実行使用メモリ | 33,672 KB |
最終ジャッジ日時 | 2025-05-14 13:14:37 |
合計ジャッジ時間 | 3,918 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 1 |
other | AC * 15 WA * 9 |
コンパイルメッセージ
main.cpp: In function ‘int main()’: main.cpp:216:10: warning: ignoring return value of ‘int scanf(const char*, ...)’ declared with attribute ‘warn_unused_result’ [-Wunused-result] 216 | scanf("%d %d", &N, &M); | ~~~~~^~~~~~~~~~~~~~~~~ main.cpp:219:14: warning: ignoring return value of ‘int scanf(const char*, ...)’ declared with attribute ‘warn_unused_result’ [-Wunused-result] 219 | scanf("%d %d", &u, &v); | ~~~~~^~~~~~~~~~~~~~~~~ main.cpp:272:10: warning: ignoring return value of ‘int scanf(const char*, ...)’ declared with attribute ‘warn_unused_result’ [-Wunused-result] 272 | scanf("%d", &Q); // Read number of queries | ~~~~~^~~~~~~~~~ main.cpp:275:14: warning: ignoring return value of ‘int scanf(const char*, ...)’ declared with attribute ‘warn_unused_result’ [-Wunused-result] 275 | scanf("%d %d", &x, &y); // Read query pair (x, y) | ~~~~~^~~~~~~~~~~~~~~~~
ソースコード
#include <vector> #include <stack> #include <algorithm> #include <set> #include <cstdio> #include <vector> // Make sure vector is included using namespace std; // Constants and global variables const int MAXN = 50005; // Maximum number of vertices const int MAXM = 100005; // Maximum number of edges // Maximum possible nodes in block-cut tree (N vertices + M blocks worst case) // Increased size slightly for safety buffer (+5) const int MAX_TREE_NODES = MAXN + MAXM + 5; vector<int> adj[MAXN + 1]; // Original graph adjacency list (1-based indexing) int N, M; // Number of vertices and edges int dfn[MAXN + 1], low[MAXN + 1], timer; // Discovery time and low-link value for Tarjan's stack<pair<int, int>> st; // Stack to store edges for identifying BCCs bool is_articulation[MAXN + 1]; // Flags if a vertex is an articulation point vector<int> tree_adj[MAX_TREE_NODES]; // Block-cut tree adjacency list // Maps original vertex ID to its corresponding node ID in the block-cut tree. // If vertex `u` is an AP, its ID is `u`. Otherwise, it's the ID of the block it belongs to. int tree_node_id[MAXN + 1]; // Flags if a node in the block-cut tree corresponds to an articulation point (C-node) bool is_C_node[MAX_TREE_NODES]; int block_cnt = 0; // Counter for blocks (BCCs) found /** * @brief Tarjan's algorithm to find articulation points and build the Block-Cut Tree. * Finds BiConnected Components (BCCs) and constructs the tree where nodes represent * articulation points (C-nodes) and BCCs (B-nodes). * * @param u Current vertex being visited. * @param p Parent of u in DFS tree (-1 if root). */ void find_bccs_and_build_tree_tarjan(int u, int p = -1) { dfn[u] = low[u] = ++timer; int children = 0; // Count children in DFS tree for root AP check for (int v : adj[u]) { if (v == p) continue; // Skip edge back to parent if (dfn[v] > 0) { // If v is already visited low[u] = min(low[u], dfn[v]); // Update low[u] using back edge if (dfn[v] < dfn[u]) { // If it's a back edge to an ancestor st.push({u, v}); // Push edge onto stack } } else { // If v is not visited children++; st.push({u, v}); // Push edge onto stack before exploring v find_bccs_and_build_tree_tarjan(v, u); // Recursive DFS call low[u] = min(low[u], low[v]); // Update low[u] based on child's low-link value // Check if u is an articulation point based on child v if ((p != -1 && low[v] >= dfn[u]) || (p == -1 && children > 1)) { if (!is_articulation[u]) { // Mark only once and assign properties is_articulation[u] = true; tree_node_id[u] = u; // AP node ID is its vertex ID is_C_node[u] = true; // Mark it as a C-node } } // If low[v] >= dfn[u], it indicates that the component involving edge (u,v) // and the subtree rooted at v forms a BCC. if (low[v] >= dfn[u]) { block_cnt++; int block_node_id = N + block_cnt; // Assign a new ID for this block node is_C_node[block_node_id] = false; // It's a B-node set<int> bcc_vertices; // Store vertices in this BCC to avoid duplicates pair<int, int> edge; // Pop edges from stack until edge (u,v) is popped. These edges form the BCC. do { if (st.empty()) break; // Safety check edge = st.top(); st.pop(); bcc_vertices.insert(edge.first); bcc_vertices.insert(edge.second); } while (!((edge.first == u && edge.second == v) || (edge.first == v && edge.second == u))); // Check both directions for edge {u,v} // Add edges in the block-cut tree between the block node and APs within the BCC. // Map non-AP vertices to the block node ID. for (int node : bcc_vertices) { if (is_articulation[node]) { // Connect AP node `node` and block node `block_node_id` tree_adj[node].push_back(block_node_id); tree_adj[block_node_id].push_back(node); is_C_node[node] = true; // Ensure AP is marked as C-node } else { // Non-AP vertices belong to exactly one block. Map them if not already mapped. if(tree_node_id[node] == 0) { tree_node_id[node] = block_node_id; } } } } } } } // LCA related structures and functions int depth[MAX_TREE_NODES]; // Depth of each node in the block-cut tree int parent[MAX_TREE_NODES][20]; // Binary lifting table for LCA: parent[i][j] is 2^j-th ancestor of i // Stores the count of C-nodes on the path from the root to node i int path_C_count[MAX_TREE_NODES]; /** * @brief Performs DFS on the block-cut tree to compute depths, parents for LCA, * and prefix sums of C-nodes count along paths from the root. * @param u Current node ID in BCT. * @param p Parent node ID in BCT DFS tree (-1 for root). * @param d Current depth. */ void dfs_lca(int u, int p, int d) { depth[u] = d; parent[u][0] = p; path_C_count[u] = (is_C_node[u] ? 1 : 0); // Add 1 if current node is C-node if(p != -1) { // Index check for safety, although p should always be valid if called correctly if (p >= 1 && p < MAX_TREE_NODES) { path_C_count[u] += path_C_count[p]; // Add parent's path count } } // Precompute ancestors for binary lifting for (int i = 1; i < 20; ++i) { if (parent[u][i-1] != -1) { parent[u][i] = parent[parent[u][i-1]][i-1]; // 2^i ancestor is 2^(i-1) ancestor of 2^(i-1) ancestor } else { parent[u][i] = -1; // No ancestor at this level } } // Recurse for children for (int v : tree_adj[u]) { if (v != p) { // Avoid going back up to parent dfs_lca(v, u, d + 1); } } } /** * @brief Computes the Lowest Common Ancestor (LCA) of two nodes in the block-cut tree using binary lifting. * @param u First node ID. * @param v Second node ID. * @return The node ID of the LCA of u and v, or -1 if invalid input or nodes are in different components. */ int lca(int u, int v) { // Basic validity checks if (u <= 0 || v <= 0 || u >= MAX_TREE_NODES || v >= MAX_TREE_NODES || depth[u] == -1 || depth[v] == -1) return -1; // Ensure u is the deeper node if (depth[u] < depth[v]) swap(u, v); // Lift node u up until it's at the same depth as v for (int i = 19; i >= 0; --i) { if (parent[u][i] != -1 && depth[parent[u][i]] >= depth[v]) { u = parent[u][i]; } } // If u and v are now the same node, it's the LCA if (u == v) return u; // Lift u and v up together maintaining the property that their parents are different // until their parents become the same. for (int i = 19; i >= 0; --i) { if (parent[u][i] != -1 && parent[v][i] != -1 && parent[u][i] != parent[v][i]) { u = parent[u][i]; v = parent[v][i]; } } // The parent of u (and v) is now the LCA return parent[u][0]; } /** * @brief Initializes data structures required for LCA queries. * It computes depths, parent pointers, and path C-node counts for all nodes in the BCT. */ void initialize_lca() { int max_node_id = N + block_cnt; // Maximum node ID used in the BCT // Initialize depth to -1 to mark nodes as unvisited by LCA DFS fill(depth, depth + max_node_id + 1, -1); // Initialize parent table with -1 for (int i = 1; i <= max_node_id; ++i) { for (int j = 0; j < 20; ++j) { parent[i][j] = -1; } } // Since the original graph is connected, the block-cut tree is connected. // We can start DFS from any valid node to cover the entire tree. for (int i = 1; i <= max_node_id; ++i) { bool node_exists = false; // Check if node 'i' actually exists in our construction if (i <= N) { // If ID corresponds to an original vertex if (is_articulation[i]) node_exists = true; // APs retain their ID else if(tree_node_id[i] != 0) node_exists = true; // Non-APs are mapped to a block ID } else { // If ID corresponds to a block node if (i <= N + block_cnt) node_exists = true; // Check if block ID is valid } // If node exists and hasn't been visited by LCA DFS yet, start DFS from here if (node_exists && depth[i] == -1) { dfs_lca(i, -1, 0); // Start DFS from node 'i' with parent -1 and depth 0 } } } int main() { scanf("%d %d", &N, &M); for (int i = 0; i < M; ++i) { int u, v; scanf("%d %d", &u, &v); adj[u].push_back(v); adj[v].push_back(u); } // Initialize Tarjan's algorithm related arrays timer = 0; fill(dfn + 1, dfn + N + 1, 0); fill(low + 1, low + N + 1, 0); fill(is_articulation + 1, is_articulation + N + 1, false); fill(tree_node_id + 1, tree_node_id + N + 1, 0); fill(is_C_node, is_C_node + MAX_TREE_NODES, false); // Initialize is_C_node for all potential node IDs // Run Tarjan's algorithm starting from each unvisited vertex // This handles potentially disconnected graphs, though problem states graph is connected. for(int i=1; i<=N; ++i) { if(dfn[i] == 0) { // If vertex i not yet visited find_bccs_and_build_tree_tarjan(i); // After the main DFS call for a component returns, check if stack has remaining edges. // This handles the case where the entire component (or the part containing the root) is a single BCC. if (!st.empty()) { block_cnt++; int block_node_id = N + block_cnt; is_C_node[block_node_id] = false; // Last block node set<int> bcc_vertices; while(!st.empty()) { pair<int, int> edge = st.top(); st.pop(); bcc_vertices.insert(edge.first); bcc_vertices.insert(edge.second); } // Connect the last block node correctly for (int node : bcc_vertices) { if (is_articulation[node]) { tree_adj[node].push_back(block_node_id); tree_adj[block_node_id].push_back(node); is_C_node[node] = true; // Ensure AP marked as C-node } else { // Map non-AP node if not already mapped if(tree_node_id[node] == 0) { tree_node_id[node] = block_node_id; } } } } } } // Initialize LCA data structures after building the block-cut tree initialize_lca(); int Q; scanf("%d", &Q); // Read number of queries while (Q--) { int x, y; scanf("%d %d", &x, &y); // Read query pair (x, y) if (x == y) { // If x and y are the same vertex printf("0\n"); // Removing any other vertex z cannot disconnect x from itself continue; } // Get corresponding node IDs in the block-cut tree // If vertex is AP, its ID is vertex ID itself. Otherwise, use mapped block ID. int tx = (is_articulation[x] ? x : tree_node_id[x]); int ty = (is_articulation[y] ? y : tree_node_id[y]); // Basic check if nodes were successfully mapped. Should not fail for connected graph N>=3. if (tx == 0 || ty == 0) { printf("0\n"); // Indicates an issue or edge case not handled continue; } // If x and y map to the same node in BCT, they are in the same block. // Removing any single vertex z != x, y cannot disconnect them. if (tx == ty) { printf("0\n"); continue; } // Find the LCA of tx and ty in the block-cut tree int ancestor = lca(tx, ty); if (ancestor == -1) { // If LCA is -1, indicates an error (e.g., disconnected tree, invalid nodes) printf("0\n"); continue; } // Calculate the number of C-nodes (APs) on the path between tx and ty using path counts // Count(tx to root) + Count(ty to root) - 2 * Count(LCA to root) int count = path_C_count[tx] + path_C_count[ty] - 2 * path_C_count[ancestor]; // If the LCA itself is a C-node, it was counted in both paths and subtracted twice. Add it back once. if (is_C_node[ancestor]) { count++; } // We need to count vertices z such that z != x and z != y. // The path count includes all C-nodes on the path. If x or y are APs, // their corresponding C-nodes are endpoints of the path tx -- ty. // We must subtract them from the count. if (is_articulation[x]) { count--; } if (is_articulation[y]) { count--; } // The final count must be non-negative. printf("%d\n", max(0, count)); } return 0; }