Submission #1309743

#TimeUsernameProblemLanguageResultExecution timeMemory
1309743ayuxhkumxr22Usmjeri (COCI17_usmjeri)C++20
0 / 140
236 ms62056 KiB
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN = 300005;
const int MOD = 1e9 + 7;

int N, M;
vector<int> adj[MAXN];
int depth[MAXN], up[MAXN][20];
int parent[MAXN]; // Original tree parent

// DSU for "Same" constraints (merging vertical paths)
// dsu[u] represents the edge (u, parent[u])
int dsu[MAXN]; 

int find(int x) {
    if (x == dsu[x]) return x;
    return dsu[x] = find(dsu[x]);
}

// Conflict Graph for "Different" constraints
vector<int> conflict_adj[MAXN];
int color[MAXN]; // 0: unvisited, 1: color A, 2: color B
bool impossible = false;

// 1. Standard LCA Preprocessing
void dfs_lca(int u, int p, int d) {
    depth[u] = d;
    up[u][0] = p;
    parent[u] = p;
    for (int i = 1; i < 20; i++) {
        up[u][i] = up[up[u][i-1]][i-1];
    }
    for (int v : adj[u]) {
        if (v != p) dfs_lca(v, u, d + 1);
    }
}

int get_lca(int a, int b) {
    if (depth[a] < depth[b]) swap(a, b);
    for (int i = 19; i >= 0; i--) {
        if (depth[a] - (1 << i) >= depth[b]) {
            a = up[a][i];
        }
    }
    if (a == b) return a;
    for (int i = 19; i >= 0; i--) {
        if (up[a][i] != up[b][i]) {
            a = up[a][i];
            b = up[b][i];
        }
    }
    return up[a][0];
}

// 2. Merge vertical path u -> lca
// Optimization: We use DSU to skip already-merged edges.
// We merge edge u with edge parent[u].
void merge_path(int u, int lca) {
    // We want to merge all edges from u up to lca.
    // In DSU, 'u' represents edge (u, parent[u]).
    // We walk up. If u is already merged with parent, find(u) will take us higher.
    
    u = find(u); // Start from the highest representative of u's current block
    while (depth[u] > depth[lca]) {
        int p = find(parent[u]); // The edge above u
        if (p != u) {
            dsu[u] = p; // Merge u into p (u becomes child of p in DSU)
        }
        u = find(u); // Move to the new representative (higher up)
    }
}

// 3. Bipartite Coloring DFS
void dfs_color(int u, int c) {
    color[u] = c;
    for (int v : conflict_adj[u]) {
        if (color[v] == 0) {
            dfs_color(v, 3 - c); // Toggle 1 <-> 2
        } else if (color[v] == c) {
            impossible = true;
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> N >> M;
    for (int i = 0; i < N - 1; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    // Root at 1
    dfs_lca(1, 1, 0);

    // Initialize DSU: each node u represents edge (u, parent[u])
    // Note: Node 1 has no edge above it, so dsu[1] is a dummy/root.
    for (int i = 1; i <= N; i++) dsu[i] = i;

    vector<pair<int, int>> constraints;
    
    for (int i = 0; i < M; i++) {
        int a, b;
        cin >> a >> b;
        int lca = get_lca(a, b);
        
        // Step A: Merge Vertical Paths (SAME constraint)
        // Merge edges on A -> LCA
        merge_path(a, lca);
        // Merge edges on B -> LCA
        merge_path(b, lca);
        
        constraints.push_back({a, b});
    }

    // Step B: Build Conflict Graph (DIFF constraint)
    // Constraint is between the representative of A-side and B-side
    for (auto p : constraints) {
        int a = p.first;
        int b = p.second;
        int lca = get_lca(a, b);
        
        // If a == lca, purely vertical path, no conflict generated.
        // Otherwise, the relevant edge is the one representing the group ending at 'a'.
        // That edge-group is simply find(a).
        
        if (a != lca && b != lca) {
            int root_a = find(a);
            int root_b = find(b);
            
            // Add XOR constraint between these two groups
            conflict_adj[root_a].push_back(root_b);
            conflict_adj[root_b].push_back(root_a);
        }
    }

    // Step C: Count Components & Check Bipartite
    long long ans = 1;
    int components = 0;

    // We only care about nodes 2..N (edges). Node 1 is dummy.
    // Also, we only iterate over DSU roots to save time/logic, 
    // or just iterate all and skip visited.
    
    for (int i = 2; i <= N; i++) {
        // Only process the representative of a set
        if (dsu[i] == i && color[i] == 0) {
            components++;
            dfs_color(i, 1);
        }
    }

    if (impossible) {
        cout << 0 << endl;
    } else {
        // Answer is 2^k
        long long res = 1;
        for (int i = 0; i < components; i++) {
            res = (res * 2) % MOD;
        }
        cout << res << endl;
    }

    return 0;
}
#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...
#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...