Submission #607246

#TimeUsernameProblemLanguageResultExecution timeMemory
607246jophyyjhOne-Way Streets (CEOI17_oneway)C++14
100 / 100
127 ms24196 KiB
/** * Notes during contest. * * ------ A ------ * Looks like that it's about strongly-connected components, cycles or bridges. I * think we can detect all cycles and merge all those nodes into one. The resulting * graph would be a tree, so we can then use a difference array to find each edge's * direction. Note that any edge in a cycle is B, and any edge not in cycles is a * "bridge". * !IMPORTANT! (Reminded by tinjyu during contest) !IMPORTANT! * Graph may not be CONNECTED. * [After contest] I realized that i forgot how to use a difference array on a tree. * That's why i couldn't solve the last testcase. Noooo.. * * PS1 I think this is the problem for which i wrote the most code.. In general, I * think CEOI 2017 Day 1 is more technical, instead of requiring ingenius thoughts. * PS2 It turns out that we don't even need to compute LCA. (+1/-1 cancels out) * * ------ B ------ * We iterate through all cost k. We can take at most k bets. If we can guarantee * a profit of p, min(#A_is_the_outcome, #B_is_the_outcome) >= p + k. Binary search * is ok. * For each k, we wanna find the max achievable for * min(#A_is_outcome, #B_is_outcome) - k. * * ------ C ------ * We have a tree in which a mouse runs. [S1] is just a brute-force. * * Time Complexity 1: O(n + m + p) * Time Complexity 2: O(n * log(n * VAL_MAX) * log(n)) * Time Complexity 3: O() * Implementation 1 */ #include <bits/stdc++.h> typedef int64_t int_t; typedef std::vector<int_t> vec; const int_t INF = 0x3f3f3f3f3f3f; const int_t LCA_HEIGHT = 18; inline int_t log2(int_t k) { return sizeof(int_t) * 8 - __builtin_clzll(k) - 1; } // a DSU impl that allows assigning a value to each set struct DSU { vec set, rank; int_t components; DSU(int_t n) { set.resize(n); rank.resize(n); for (int_t k = 0; k < n; k++) set[k] = k, rank[k] = 1; components = n; } inline int_t find(int_t k) { if (set[k] == k) return k; return set[k] = find(set[k]); } bool merge(int_t a, int_t b) { // merge b into a (keep a's value) a = find(a), b = find(b); if (a == b) return false; if (rank[b] > rank[a]) std::swap(a, b); set[b] = a, rank[a] += rank[b], components--; return true; } }; struct edge_t { int_t a, b; }; struct neighb_t { int_t neighb, edge_idx; }; typedef std::vector<neighb_t> adj_list_t; void dfs1(int_t k, int_t parent, const std::vector<adj_list_t>& graph, std::vector<bool>& tree_edge, vec& depth, std::vector<bool>& visited) { visited[k] = true; if (parent != -1) depth[k] = depth[parent] + 1; else depth[k] = 0; for (const neighb_t& child : graph[k]) { if (child.neighb == parent || visited[child.neighb]) continue; tree_edge[child.edge_idx] = true; dfs1(child.neighb, k, graph, tree_edge, depth, visited); } } void dfs2(int_t k, int_t parent, const std::vector<adj_list_t>& graph, const std::vector<bool>& tree_edge, vec& across, const vec& depth, std::vector<bool>& is_bridge) { across[k] = 0; int_t backward_up = 0, backward_down = 0; for (const neighb_t& child : graph[k]) { if (tree_edge[child.edge_idx] && child.neighb != parent) { dfs2(child.neighb, k, graph, tree_edge, across, depth, is_bridge); across[k] += across[child.neighb]; } else { if (depth[child.neighb] > depth[k]) backward_down++; else if (depth[child.neighb] < depth[k]) backward_up++; } } if (parent != -1) // the tree edge linking to parent backward_up--; across[k] += backward_up - backward_down; assert(across[k] >= 0); if (parent != -1 && across[k] == 0) { for (const neighb_t& child : graph[k]) { if (child.neighb == parent) is_bridge[child.edge_idx] = true; } } } // Find bridges in the graph with a DFS tree // I learnt this from a tutorial on codeforces (about DFS trees) std::vector<bool> find_bridge(int_t n, int_t m, const std::vector<edge_t>& original_graph) { std::vector<adj_list_t> graph(n); for (int_t k = 0; k < m; k++) { int_t a = original_graph[k].a, b = original_graph[k].b; graph[a].push_back(neighb_t{b, k}); graph[b].push_back(neighb_t{a, k}); } vec depth(n), across(n); std::vector<bool> tree_edge(m, false); std::vector<bool> visited(n, false); std::vector<bool> is_bridge(m, false); for (int_t k = 0; k < n; k++) { if (!visited[k]) { dfs1(k, -1, graph, tree_edge, depth, visited); dfs2(k, -1, graph, tree_edge, across, depth, is_bridge); } } return is_bridge; } int_t dfs3(int_t k, int_t parent, const std::vector<adj_list_t>& graph, std::vector<bool>& visited, const vec& diff, vec& start) { visited[k] = true; int_t sum = 0, up_edge = -1; for (const neighb_t& neighb : graph[k]) { if (neighb.neighb == parent) { up_edge = neighb.edge_idx; } else { sum += dfs3(neighb.neighb, k, graph, visited, diff, start); } } sum += diff[k]; if (sum > 0) start[up_edge] = parent; else if (sum < 0) start[up_edge] = k; return sum; } int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int_t n, m; std::cin >> n >> m; std::vector<edge_t> original_graph(m); for (int_t k = 0; k < m; k++) { std::cin >> original_graph[k].a >> original_graph[k].b; original_graph[k].a--, original_graph[k].b--; } std::vector<bool> is_bridge = find_bridge(n, m, original_graph); DSU compress(n); // used to compress the nodes for (int_t k = 0; k < m; k++) { if (!is_bridge[k]) compress.merge(original_graph[k].a, original_graph[k].b); } vec map(n, -1); // mapping an original node into one in our new graph for (int_t k = 0, new_graph_top = 0; k < n; k++) { if (map[compress.find(k)] == -1) map[compress.find(k)] = new_graph_top++; map[k] = map[compress.find(k)]; } std::vector<adj_list_t> graph(compress.components); for (int_t k = 0; k < m; k++) { int_t a = map[original_graph[k].a], b = map[original_graph[k].b]; if (a != b) { graph[a].push_back(neighb_t{b, k}); graph[b].push_back(neighb_t{a, k}); } } n = compress.components; int_t pairs; std::cin >> pairs; vec diff(n, 0); // difference array on a tree for (int_t k = 0; k < pairs; k++) { int_t src, dst; std::cin >> src >> dst; src = map[src - 1], dst = map[dst - 1]; diff[src]--, diff[dst]++; } std::vector<bool> visited(n, false); vec start(m, -1); // start[]: edge a -> b, start = a. for (int_t k = 0; k < n; k++) { if (!visited[k]) dfs3(k, -1, graph, visited, diff, start); } std::string ans(m, 'B'); for (int_t k = 0; k < m; k++) { if (is_bridge[k]) { if (start[k] != -1) { if (map[original_graph[k].a] == start[k]) ans[k] = 'R'; else ans[k] = 'L'; } else { ans[k] = 'B'; } } } std::cout << ans << '\n'; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...