제출 #607246

#제출 시각아이디문제언어결과실행 시간메모리
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...