Codeforces - 845G - Shortest Path Problem?(DFS + 线性基)

题目链接:https://codeforces.com/contest/845/problem/G

7.1 题意

给定一个 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \le n \le 10^5) n(1n105) 个节点和 m ( n − 1 ≤ m ≤ 1 0 5 ) m(n-1 \le m \le 10^5) m(n1m105) 条边的无向连通图,定义一条路径的长度为这条路径上边权的异或和。

问从 1 1 1 点到 n n n 点的最短路径长度。

7.2 解题过程

首先对整个图跑一遍 DFS,这时会生成一棵树。然后会发现某些边会构成一个环。

则答案转化为树上 1 1 1 n n n 的路径异或上某些环的权值之后的最小值。

我们将每个环的权值丢进一个线性基中,这样最后线性基会构成 64 64 64 个线性无关的向量。

对于第 i ( 0 ≤ i ≤ 63 ) i(0 \le i \le 63) i(0i63) 号向量,第一个 1 1 1 出现的位置一定不会高于第 i i i 位(这里的位是从低向高记的)。

设答案为 a n s ans ans,最初 a n s ans ans 为树上 1 1 1 n n n 路径的长度。

之后从高位向低位枚举向量,对 a n s ans ans 尝试进行异或,若发现答案减小,则更新答案。

时间复杂度为: O ( 64 ⋅ n ) O(64 \cdot n) O(64n)

7.3 错误点

  1. 读入边的时候千万不要将循环上界误写成 n n n
  2. 最后一定要从高位向低位枚举向量,否则不能保证答案最优。

7.4 代码

struct linear_basis {
    ll a[65], b[65];
    ll cnt;
    bool flag;
    linear_basis() {
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        cnt = 0;
        flag = false;
    }
    void add(ll x) {
        for (int i = 63; i >= 0; i--) {
            if (x & (1LL << i)) {
                if (a[i]) {
                    x ^= a[i];
                } else {
                    a[i] = x;
                    return;
                }
            }
        }
        flag = true;
    }
    ll query_max() {
        ll res = 0;
        for (int i = 63; i >= 0; i--) {
            res = max(res, res ^ a[i]);
        }
        return res;
    }
    ll query_min() {
        ll res = 0;
        for (int i = 0; i <= 63; i++) {
            if (a[i]) {
                res = a[i];
                break;
            }
        }
        return res;
    }
    // before the queries of kth_element!
    void re_build() {
        for (int i = 63; i >= 0; i--) {
            for (int j = i - 1; j >= 0; j--) {
                if (a[i] & (1LL << j)) {
                    a[i] ^= a[j];
                }
            }
        }
        for (int i = 0; i <= 63; i++) {
            if (a[i]) {
                b[cnt++] = a[i];
            }
        }
    }
    ll kth_element(ll k) {
        if (flag) k--;
        ll res = 0;
        if (k >= (1LL << cnt)) {
            return -1;
        }
        for (int i = 63; i >= 0; i--) {
            if (k & (1LL << i)) {
                res ^= b[i];
            }
        }
        return res;
    }
} loop;
vector<pii> G[maxn];
ll d[maxn];
bool vis[maxn];
void addedge(int u, int v, ll w) {
    G[u].push_back({v, w});
}
void dfs(int u) {
    vis[u] = true;
    for (auto ed: G[u]) {
        int v = ed.first;
        int w = ed.second;
        if (!vis[v]) {
            d[v] = (d[u] ^ w);
            dfs(v);
        } else {
            loop.add(d[u] ^ d[v] ^ w);
        }
    }
}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v;
        ll w;
        scanf("%d%d%lld", &u, &v, &w);
        addedge(u, v, w);
        addedge(v, u, w);
    }
    dfs(1);
    ll ans = d[n];
    for (int i = 63; i >= 0; i--) {
        ans = min(ans, ans ^ loop.a[i]);
    }
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(图论,-,树,图论,-,最短路,数学,-,线性基)