Submission #1206657

#TimeUsernameProblemLanguageResultExecution timeMemory
1206657oceanTeam Coding (EGOI24_teamcoding)C++17
54 / 100
4094 ms40264 KiB
// Standard library includes #include <iostream> // For input/output operations (cin, cout) #include <vector> // For dynamic arrays (std::vector) #include <algorithm>// For std::min, std::max, etc. #include <map> // For std::map (used in precomputing C[k][l]) // Using standard namespace for convenience, to avoid writing std:: repeatedly using namespace std; // INF is a large value used to initialize min_s_global (minimum switches). // Team size P and switches S are counts of people, at most N. // current_P_val can accumulate sums up to N*N in theory (N levels, each term min(N,N)), so long long is needed. // Final max_p_global and min_s_global will be at most N. Using long long for them for consistency. const long long INF = 4e18; // --- Global Variables --- // These variables are accessible throughout the program. // Adjacency list to represent the company hierarchy as a tree. // adj[i] will store a vector of employee IDs who directly report to employee i. vector<int> adj[100005]; // emp_lang[i] stores the preferred programming language of employee i. int emp_lang[100005]; // level[i] stores the depth of employee i in the tree (CEO/root is at level 0). int level[100005]; // subtree_sz[i] stores the number of employees in the subtree rooted at employee i (including i). // This is used by the Sack algorithm to find the "big child". int subtree_sz[100005]; // n_nodes: total number of employees. // k_langs: total number of different programming languages. int n_nodes, k_langs; // max_p_global: stores the maximum team size found so far across all possible team leads. long long max_p_global = 0; // min_s_global: stores the minimum number of switches required to achieve max_p_global. long long min_s_global = INF; // Sparse representation of C[k][l]: // C[k][l] would be the total count of employees in the *entire company* // who prefer language 'k' and are at level 'l'. // Instead of a dense KxN matrix (which would be too large for memory), // relevant_Cs_at_level[l] stores a list of pairs {language_idx, count}. // This list only includes languages that actually have 'count' > 0 employees at level 'l'. // The total number of pairs stored across all levels is at most n_nodes. vector<vector<pair<int, int>>> relevant_Cs_at_level; // Global arrays for the Sack algorithm's state: // As the Sack algorithm processes a subtree (rooted at some 'u'), these arrays // are updated to reflect aggregated information about the nodes currently // considered "in the Sack's bag" (i.e., nodes from S_u). // freq_level[l]: stores the count of nodes currently in the Sack bag that are at global tree level 'l'. // Represents |{x in S_u | level[x]==l}| for the current S_u. int freq_level[100005]; // current_I_val[lang_idx]: stores the count of nodes currently in the Sack bag // that prefer language 'lang_idx'. // Represents sum_{x in S_u, emp_lang[x]==lang_idx} 1. This is used to find initial_members_u. long long current_I_val[100005]; // current_P_val[k_test]: for a given language k_test, this stores the sum over all levels l of: // min(freq_level[l], C[k_test][l]). // This represents the total team size if k_test was the project language, based on the current Sack bag. long long current_P_val[100005]; // --- Helper DFS Functions (executed once at the beginning) --- // Computes the level (depth) of each employee in the tree. // CEO (node 0) is at level 0. // 'u': current employee being visited. // 'p': parent of 'u' in the DFS traversal (to avoid going back up). // 'l': current level to assign to 'u'. void dfs_levels(int u, int p, int l) { level[u] = l; // Assign current level 'l' to employee 'u' for (int v : adj[u]) { // For each direct report 'v' of 'u' if (v == p) continue; // Skip the parent node dfs_levels(v, u, l + 1); // Recursively call for child 'v' at the next level (l+1) } } // Computes the size of the subtree rooted at each employee 'u'. // Subtree size includes 'u' itself. // This is needed by the Sack algorithm to identify the "big child" for optimization. // 'u': current employee being visited. // 'p': parent of 'u'. void dfs_size(int u, int p) { subtree_sz[u] = 1; // Employee 'u' itself contributes 1 to its subtree size for (int v : adj[u]) { // For each direct report 'v' of 'u' if (v == p) continue; // Skip parent dfs_size(v, u); // Recursively compute subtree size for child 'v' subtree_sz[u] += subtree_sz[v]; // Add child's subtree size to 'u's subtree size } } // --- Sack Algorithm Core Functions --- // This function updates the global Sack state arrays (freq_level, current_P_val, current_I_val) // when 'node_x' is either added to or removed from the Sack's consideration. // 'node_x': the employee node being processed. // 'val_sign': +1 if adding node_x, -1 if removing node_x. void update_sack_data(int node_x, int val_sign) { int lvl_x = level[node_x]; // Get the level of node_x int lang_x = emp_lang[node_x]; // Get the language of node_x // 1. Update freq_level and get old/new values // freq_level[lvl_x] tracks how many nodes currently in the Sack bag are at this level. long long old_freq_at_lvl_x = freq_level[lvl_x]; // Count before this node_x is processed freq_level[lvl_x] += val_sign; // Add/remove node_x's presence at this level long long new_freq_at_lvl_x = freq_level[lvl_x]; // Count after node_x is processed // 2. Update current_P_val[k_test] for all relevant languages k_test. // current_P_val[k_test] = sum over all levels l of: min(freq_level[l], C[k_test][l]). // When freq_level[lvl_x] changes, only the term for l=lvl_x in this sum changes. // The change is: min(new_freq_at_lvl_x, C[k_test][lvl_x]) - min(old_freq_at_lvl_x, C[k_test][lvl_x]). // We only need to do this for languages k_test that actually have employees // at lvl_x in the whole company (i.e., C[k_test][lvl_x] > 0). // This is where `relevant_Cs_at_level` (our sparse C matrix) is used. if (lvl_x < (int)relevant_Cs_at_level.size()) { // Boundary check for level index // Iterate through languages k_test that have employees at lvl_x for (auto const& pair_kc : relevant_Cs_at_level[lvl_x]) { int k_test = pair_kc.first; // The language k_test long long C_val_k_test_lvl_x = pair_kc.second; // C[k_test][lvl_x] (total employees in company with lang k_test at lvl_x) long long term_change = 0; // Subtract the contribution of lvl_x to P_val[k_test] based on old_freq_at_lvl_x if (old_freq_at_lvl_x > 0) { // If there were nodes at this level in the bag before term_change -= min(old_freq_at_lvl_x, C_val_k_test_lvl_x); } // Add the contribution of lvl_x to P_val[k_test] based on new_freq_at_lvl_x if (new_freq_at_lvl_x > 0) { // If there are nodes at this level in the bag now term_change += min(new_freq_at_lvl_x, C_val_k_test_lvl_x); } // Apply the net change to current_P_val for this language k_test current_P_val[k_test] += term_change; } } // 3. Update current_I_val for the language of node_x. // current_I_val[lang_x] tracks the count of nodes in the Sack bag preferring lang_x. // This directly changes by val_sign (+1 for add, -1 for remove). current_I_val[lang_x] += val_sign; } // Helper DFS to apply update_sack_data to all nodes in a given subtree. // This is used by the Sack algorithm to add contributions of small children's subtrees // or to clear data from a subtree when it's no longer needed. // 'u': root of the subtree to process. // 'p': parent of 'u'. // 'val_sign': +1 to add subtree nodes to Sack state, -1 to remove. void dfs_process_subtree(int u, int p, int val_sign) { update_sack_data(u, val_sign); // Process the current node 'u' for (int v : adj[u]) { // For each child 'v' of 'u' if (v == p) continue; // Skip parent dfs_process_subtree(v, u, val_sign); // Recursively process child's subtree } } // The main Sack (DSU on Tree) recursive function. // This function efficiently calculates properties for all subtrees. // 'u': current employee node (acting as the root of the subtree currently being aggregated). // 'p': parent of 'u' in the DFS tree. // 'keep': boolean flag. If true, the Sack state data for u's subtree should be preserved // after this function call (this is for the "big child" optimization). // If false, the data will be cleared. void dfs_sack(int u, int p, bool keep) { // 1. Find the "big child" of 'u'. // The big child is the child with the largest subtree size. // Processing the big child last and keeping its data saves computations. int max_child_sz = -1, big_child = -1; for (int v : adj[u]) { if (v == p) continue; if (subtree_sz[v] > max_child_sz) { max_child_sz = subtree_sz[v]; big_child = v; } } // 2. Recursively call dfs_sack for all "small children" of 'u'. // The 'keep' flag is set to 'false', so their data is cleared after their respective calls. // The global Sack state arrays are effectively empty before processing the big child (or 'u' if no children). for (int v : adj[u]) { if (v == p || v == big_child) continue; // Skip parent and big_child dfs_sack(v, u, false); } // 3. Recursively call dfs_sack for the "big child" (if one exists). // The 'keep' flag is set to 'true'. This means after this call, the global Sack state arrays // (freq_level, current_P_val, current_I_val) will hold the aggregated data // for the big child's entire subtree. if (big_child != -1) { dfs_sack(big_child, u, true); } // 4. Add employee 'u' (the current subtree root) itself to the Sack state. // The Sack state now includes 'u' and, if a big child existed, its subtree data. update_sack_data(u, 1); // 5. Add data from all "small children's" subtrees to the current Sack state. // `dfs_process_subtree` is used here because we need to iterate through each small child's // subtree and add all its nodes. The data for these small children was cleared earlier. for (int v : adj[u]) { if (v == p || v == big_child) continue; // Skip parent and big_child dfs_process_subtree(v, u, 1); // Add nodes of this small child's subtree } // 6. QUERY STEP: // At this precise moment, the global Sack state arrays (freq_level, current_P_val, current_I_val) // accurately reflect the aggregated information for the *entire subtree S_u* rooted at 'u'. // We can now use these values to calculate the maximum team size (cand_P) and // minimum switches (cand_S) if 'u' were chosen as the team lead. int k_u = emp_lang[u]; // The project's programming language is u's preferred language. // cand_P is the total number of people on the team if 'u' is lead and language is k_u. // This value is directly available from current_P_val[k_u], which has accumulated // sum_levels min(|S_u at l|, C[k_u][l]). long long cand_P = current_P_val[k_u]; // cand_I is the number of initial members from S_u who prefer language k_u. // This is directly available from current_I_val[k_u]. long long cand_I = current_I_val[k_u]; // cand_S is the number of switches: total team members - initial team members. long long cand_S = cand_P - cand_I; // Update the global best result (max_p_global, min_s_global) if (cand_P > max_p_global) { // If we found a larger team size max_p_global = cand_P; min_s_global = cand_S; } else if (cand_P == max_p_global) { // If team size is the same as current max min_s_global = min(min_s_global, cand_S); // Choose the one with fewer switches } // 7. CLEANUP STEP: // If the 'keep' flag is 'false' (meaning 'u' is a small child of its caller, // or 'u' is the root of the initial dfs_sack call from main), // we must clear the data of u's entire subtree from the global Sack state arrays. // This makes sure that when 'u's sibling (or parent's processing) continues, // the Sack state is clean or reflects only the relevant parts. if (!keep) { // dfs_process_subtree with val_sign = -1 will remove all nodes in S_u // (which includes 'u' itself, its big child's subtree, and small children's subtrees // that were added in steps 4 and 5). dfs_process_subtree(u, p, -1); } } // --- Main Function --- int main() { // Speeds up C++ standard input/output operations. ios_base::sync_with_stdio(false); cin.tie(NULL); // Read N (number of employees) and K (number of languages) cin >> n_nodes >> k_langs; // Read preferred language for each employee (0 to N-1) for (int i = 0; i < n_nodes; ++i) { cin >> emp_lang[i]; } // Build the directed tree from input. adj[boss] stores a vector of children. // Employee 0 is the CEO and has no boss. // Input describes bosses for employees 1 to N-1. vector<vector<int>> adj_build(n_nodes); // Temporary adjacency list for building for (int i = 1; i < n_nodes; ++i) { // For employee 'i' (from 1 to N-1) int b_val; // b_val is the boss of employee 'i' cin >> b_val; adj_build[b_val].push_back(i); // Add 'i' as a child of its boss 'b_val' } // Copy the built tree structure to the global 'adj' array. for(int i=0; i<n_nodes; ++i) adj[i] = adj_build[i]; // 1. Perform initial DFS traversals to gather basic tree information. dfs_levels(0, -1, 0); // Calculate levels for all nodes. CEO is node 0, has no parent (-1), at level 0. dfs_size(0, -1); // Calculate subtree sizes for all nodes. Needed for Sack's big child heuristic. // 2. Precompute the sparse C[k][l] table (company-wide language counts per level). // relevant_Cs_at_level[l] will store {lang, count} pairs for level l. relevant_Cs_at_level.resize(n_nodes); // Max level is n_nodes-1, so size n_nodes is sufficient. // Use a temporary map to aggregate counts before converting to vector of pairs. // temp_counts_for_Cs[level_idx] maps language_idx to its count at that level_idx. vector<map<int, int>> temp_counts_for_Cs(n_nodes); for (int i = 0; i < n_nodes; ++i) { // For each employee if (level[i] < n_nodes) { // Level should always be < n_nodes temp_counts_for_Cs[level[i]][emp_lang[i]]++; // Increment count for their language at their level } } // Convert the map data to the final vector<vector<pair<int,int>>> structure // for efficient iteration in update_sack_data. for (int l = 0; l < n_nodes; ++l) { // For each level if (!temp_counts_for_Cs[l].empty()) { // If there are any employees at this level relevant_Cs_at_level[l].reserve(temp_counts_for_Cs[l].size()); // Pre-allocate memory (optimization) for (auto const& pair_kl : temp_counts_for_Cs[l]) { // For each {lang, count} at this level relevant_Cs_at_level[l].push_back({pair_kl.first, pair_kl.second}); } } } // 3. Initialize Sack global state arrays. // Global static arrays are typically zero-initialized by default in C++, // but explicit initialization is good for clarity and safety. for(int i=0; i<k_langs; ++i) { // Max k_langs is 10^5, arrays are sized 100005 current_P_val[i] = 0; current_I_val[i] = 0; } for(int i=0; i<n_nodes; ++i) { // Max n_nodes is 10^5, array is sized 100005 freq_level[i] = 0; } // 4. Start the Sack algorithm from the CEO (employee 0). // The 'keep' flag is 'false' for the initial call because we don't need to // preserve the data of the entire tree after the process completes. dfs_sack(0, -1, false); // 5. Output the final results. // max_p_global will hold the maximum team size. // min_s_global will hold the minimum switches for that maximum size. // If N=0 was possible, max_p_global might be 0. But N>=1, so max_p_global>=1. // min_s_global is initialized to INF and will be updated correctly. cout << max_p_global << " " << min_s_global << endl; return 0; // Indicate successful program execution }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...