結果
| 問題 |
No.1194 Replace
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 12:59:50 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 1,310 ms / 2,000 ms |
| コード長 | 13,773 bytes |
| コンパイル時間 | 1,882 ms |
| コンパイル使用メモリ | 120,500 KB |
| 実行使用メモリ | 117,436 KB |
| 最終ジャッジ日時 | 2025-05-14 13:01:43 |
| 合計ジャッジ時間 | 21,646 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 27 |
ソースコード
#include <iostream>
#include <vector>
#include <numeric>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
// Use long long for large integers to accommodate N up to 10^9 and sums up to N(N+1)/2
typedef long long ll;
// Global variables used across graph processing functions
vector<vector<int>> adj; // Adjacency list for the graph based on mapped indices
vector<int> dfn; // Discovery time in DFS (used in Tarjan's algorithm)
vector<int> low; // Lowest discovery time reachable from a node (used in Tarjan's algorithm)
vector<int> scc_id; // Stores the SCC ID assigned to each vertex (mapped index)
stack<int> S_tarjan; // Stack used by Tarjan's algorithm to keep track of visited nodes
vector<bool> onStack; // Tracks whether a vertex is currently on the Tarjan's stack
int timer; // Counter for assigning discovery times during DFS
int scc_count; // Counter for the number of Strongly Connected Components found
vector<ll> idx_to_val; // Maps an internal index (0 to num_vertices-1) back to its original potentially large integer value
vector<vector<int>> adj_scc; // Adjacency list for the condensation graph (graph of SCCs)
vector<ll> MaxVal; // Stores the maximum original value found within each SCC
vector<ll> memo; // Memoization table for the DP calculation on the condensation graph (-1 indicates value not computed yet)
/**
* @brief Performs Tarjan's algorithm to find Strongly Connected Components (SCCs) in the graph.
*
* @param u The current vertex index being visited.
*/
void tarjan(int u) {
dfn[u] = low[u] = ++timer; // Initialize discovery time and low-link value for vertex u
S_tarjan.push(u); // Push vertex u onto the stack
onStack[u] = true; // Mark vertex u as being on the stack
// Iterate through all neighbors v of vertex u in the graph
for (int v : adj[u]) {
if (dfn[v] == 0) { // If neighbor v has not been visited yet (dfn[v] == 0)
tarjan(v); // Recursively call Tarjan's algorithm on v
// After returning from the recursive call for v, update the low-link value of u.
// u can reach whatever v can reach.
low[u] = min(low[u], low[v]);
} else if (onStack[v]) { // If neighbor v has been visited and is still on the stack
// This indicates a back edge to an ancestor in the DFS tree, closing a cycle.
// Update the low-link value of u based on the discovery time of v.
// We use dfn[v] here because v is an ancestor already on the stack.
low[u] = min(low[u], dfn[v]);
}
}
// Check if vertex u is the root of an SCC
// This happens if its low-link value is equal to its discovery time.
if (low[u] == dfn[u]) {
// If u is a root, pop vertices from the stack until u is popped.
// All vertices popped belong to the same SCC.
while (true) {
int node = S_tarjan.top(); // Get the top vertex from the stack
S_tarjan.pop(); // Pop the vertex
onStack[node] = false; // Mark the vertex as removed from the stack
scc_id[node] = scc_count; // Assign the current SCC ID to this vertex
if (node == u) break; // Stop when the root u itself is popped
}
scc_count++; // Increment the SCC counter for the next SCC found
}
}
/**
* @brief Computes the maximum reachable value starting from a given SCC using Dynamic Programming with memoization.
*
* This function operates on the condensation graph, which is a Directed Acyclic Graph (DAG).
*
* @param scc_u The index (ID) of the starting SCC.
* @return The maximum value reachable from any vertex within SCC scc_u.
*/
ll computeMaxReachable(int scc_u) {
// If the result for this SCC has already been computed and stored in the memo table, return it directly.
if (memo[scc_u] != -1) {
return memo[scc_u];
}
// Initialize the maximum reachable value with the maximum value found intrinsically within the current SCC (`scc_u`).
ll max_reach_val = MaxVal[scc_u];
// Explore all SCCs (`scc_v`) that are directly reachable from `scc_u` in the condensation graph.
for (int scc_v : adj_scc[scc_u]) {
// Recursively call `computeMaxReachable` for the neighbor SCC `scc_v`.
// Update `max_reach_val` if the value reachable through `scc_v` is greater.
max_reach_val = max(max_reach_val, computeMaxReachable(scc_v));
}
// Store the computed maximum reachable value in the memoization table before returning it.
return memo[scc_u] = max_reach_val;
}
int main() {
// Use fast I/O operations
ios_base::sync_with_stdio(false);
cin.tie(NULL);
ll N; // Length of the array A
int M; // Number of candidate operations
cin >> N >> M;
vector<pair<ll, ll>> ops(M); // Stores the M operations as pairs (B_i, C_i)
set<ll> involved_vals_set; // A set to efficiently collect unique values appearing in operations (either as B_i or C_i)
set<ll> sources_set; // A set to efficiently collect unique source values (B_i) of the operations
// Read the M operations from input
for (int i = 0; i < M; ++i) {
cin >> ops[i].first >> ops[i].second; // Read B_i and C_i
// Add both B_i and C_i to the set of involved values
involved_vals_set.insert(ops[i].first);
involved_vals_set.insert(ops[i].second);
// Add B_i to the set of source values
sources_set.insert(ops[i].first);
}
// Since original values (B_i, C_i) can be large (up to N=10^9), we map them
// to contiguous integer indices [0, num_vertices-1] to build a manageable graph.
map<ll, int> val_to_idx; // Maps original value to its internal index
// `idx_to_val` is populated during mapping to allow retrieval of original values later
idx_to_val.reserve(involved_vals_set.size()); // Reserve capacity for efficiency
int current_idx = 0; // Counter for assigning indices
// Convert the set of involved values to a vector to ensure consistent iteration order for mapping
vector<ll> involved_vals_vec(involved_vals_set.begin(), involved_vals_set.end());
for (ll val : involved_vals_vec) {
val_to_idx[val] = current_idx; // Assign index `current_idx` to value `val`
idx_to_val.push_back(val); // Store `val` at index `current_idx` in the reverse map
current_idx++; // Move to the next index
}
int num_vertices = current_idx; // The total number of unique values involved gives the number of vertices in our graph
// Proceed with graph algorithms only if there are operations involving values (num_vertices > 0)
if (num_vertices > 0) {
// Build the adjacency list `adj` for the graph using the mapped indices
adj.resize(num_vertices);
for (int i = 0; i < M; ++i) {
// Check if both B_i and C_i exist in our map (they should, by construction)
if (val_to_idx.count(ops[i].first) && val_to_idx.count(ops[i].second)) {
int u = val_to_idx[ops[i].first]; // Get the index for B_i
int v = val_to_idx[ops[i].second]; // Get the index for C_i
adj[u].push_back(v); // Add a directed edge from u to v
}
}
// Initialize data structures needed for Tarjan's algorithm
dfn.assign(num_vertices, 0); // Initialize discovery times to 0 (meaning unvisited)
low.assign(num_vertices, 0); // Initialize low-link values to 0
scc_id.assign(num_vertices, -1); // Initialize SCC IDs to -1 (meaning no SCC assigned yet)
onStack.assign(num_vertices, false); // Initialize onStack status for all vertices to false
// Ensure the Tarjan stack is empty before starting (important if reusing these globals, e.g., multiple test cases)
while (!S_tarjan.empty()) S_tarjan.pop();
timer = 0; // Reset the timer for discovery times
scc_count = 0; // Reset the SCC counter
// Run Tarjan's algorithm starting from each vertex that hasn't been visited yet
for (int i = 0; i < num_vertices; ++i) {
if (dfn[i] == 0) { // If vertex i is unvisited
tarjan(i); // Start Tarjan's DFS from vertex i
}
}
// Proceed only if at least one SCC was found (scc_count > 0)
if (scc_count > 0) {
// Compute the maximum original value (`MaxVal`) present within each identified SCC
MaxVal.assign(scc_count, 0); // Initialize MaxVal for each SCC to 0
for (int i = 0; i < num_vertices; ++i) {
// Check if vertex i has been assigned to a valid SCC
if (scc_id[i] != -1) {
// Update the maximum value for the SCC that vertex i belongs to
MaxVal[scc_id[i]] = max(MaxVal[scc_id[i]], idx_to_val[i]);
}
}
// Build the condensation graph (`adj_scc`) where each node represents an SCC
adj_scc.resize(scc_count);
set<pair<int, int>> scc_edges; // Use a set to store edges between SCCs to automatically handle duplicates
// Iterate through all vertices u and their neighbors v in the original graph
for (int u = 0; u < num_vertices; ++u) {
// Skip if vertex u was not assigned to an SCC (should not happen if graph is connected/processed correctly)
if (scc_id[u] == -1) continue;
for (int v : adj[u]) {
// Skip if neighbor v was not assigned to an SCC
if (scc_id[v] == -1) continue;
// If vertex u and vertex v belong to different SCCs
if (scc_id[u] != scc_id[v]) {
// Check if an edge between these SCCs has already been added
if (scc_edges.find({scc_id[u], scc_id[v]}) == scc_edges.end()) {
// If not, add a directed edge from the SCC of u to the SCC of v in the condensation graph
adj_scc[scc_id[u]].push_back(scc_id[v]);
// Record this edge in the set to prevent adding it again
scc_edges.insert({scc_id[u], scc_id[v]});
}
}
}
}
// Compute the maximum reachable value for each SCC using Dynamic Programming on the DAG (condensation graph)
memo.assign(scc_count, -1); // Initialize the memoization table with -1 (indicating not computed)
// Iterate through all SCCs
for (int i = 0; i < scc_count; ++i) {
// If the max reachable value for SCC i hasn't been computed yet
if (memo[i] == -1) {
// Call the recursive DP function to compute it
computeMaxReachable(i);
}
}
}
}
// Calculate the initial sum of the array A = [1, 2, ..., N]
// Initial sum is Sum(i for i=1 to N) = N*(N+1)/2.
// Use 128-bit integer type for the intermediate product N*(N+1) to prevent overflow,
// as N can be up to 10^9, N*(N+1) can exceed 64 bits.
// The final result after dividing by 2 will fit within a 64-bit signed long long.
ll total_sum;
if (N == 0) {
total_sum = 0; // Handle N=0 edge case (though problem constraints state N>=2)
} else {
// Calculate N*(N+1) using 128 bits, then divide by 2
unsigned __int128 temp_sum = (unsigned __int128)N * (N + 1) / 2;
total_sum = (ll)temp_sum; // Cast the result back to long long (which is guaranteed to fit)
}
// Calculate the total change (`delta_sum`) to the initial sum caused by the operations.
// For each initial value `k` that is a source (B_i) of some operation,
// we find the maximum value `max_k` it can be transformed into,
// and add the difference (`max_k - k`) to the total change.
ll delta_sum = 0;
// Only calculate the delta sum if SCCs were actually processed (scc_count > 0)
if (scc_count > 0) {
// Iterate through each unique source value `k` identified earlier
for (ll k : sources_set) {
// Check if this source value `k` was actually involved in the graph (it should be)
if (val_to_idx.count(k)) {
int idx = val_to_idx[k]; // Get the mapped index for k
// Ensure that the index `idx` is valid and has been assigned a valid SCC ID
// Check `idx < scc_id.size()` prevents out-of-bounds access if idx is somehow invalid
if (idx < scc_id.size() && scc_id[idx] != -1) {
int scc_k = scc_id[idx]; // Get the SCC ID for the SCC containing k
// Ensure the SCC ID `scc_k` is a valid index for the memoization table `memo`
if (scc_k < memo.size()) {
// Retrieve the pre-computed maximum reachable value from the SCC containing k
ll max_k = memo[scc_k];
// Add the change (max reachable value - initial value k) to the total delta sum
delta_sum += (max_k - k);
}
}
}
}
}
// The final maximum possible sum is the initial sum plus the total calculated change (delta_sum)
total_sum += delta_sum;
// Output the final result
cout << total_sum << endl;
return 0;
}
qwewe