Processing math: 100%

bzoj 2115 [Wc2011] Xor 路径最大异或和 线性基

题目链接

题意

给定一个 n(n50000) 个点 m(m100000) 条边的无向图,每条边上有一个权值。请你求一条从 1n的路径,使得路径上的边的异或和最大。

题解

参考

https://blog.sengxian.com/algorithms/linear-basis

结论

答案=max{(某一条1n的路径的异或和)(环i1的异或和)(环i2的异或和)...}

构造性证明

我们证明,对于图中任意一个环,其异或和是可以取到的。

方法是从任意一个点u出发走到环上的某一点v,绕一圈,然后再沿着vu的路径原路返回,记路径uv的异或和为x0,环上的异或和为x1,则这一整段路的贡献即为x0x1x0=x1,即为环上的异或和。

求值

现在的问题就转化为了,在线性基中取若干个向量,使得它们与某个初始给定值的异或和取到最大

将向量从高位向低位逐个加入,使得和变大则加,否则不加,即可取到最大。

由线性基线性无关易见,对某一位的贡献只能来自于最多一个向量。

Code

#include <bits/stdc++.h>
#define maxn 200010
#define maxl 60
using namespace std;
typedef long long LL;
struct Edge { int to, ne; LL d; } edge[maxn<<1];
LL d[maxn], b[maxn];
int tot, ne[maxn], cnt;
bool vis[maxn];
void add(int u, int v, LL d) {
    edge[tot] = {v, ne[u], d};
    ne[u] = tot++;
    edge[tot] = {u, ne[v], d};
    ne[v] = tot++;
}
void dfs(int u, LL dd) {
    d[u] = dd; vis[u] = true;
    for (int i = ne[u]; ~i; i = edge[i].ne) {
        int v = edge[i].to;
        if (vis[v]) b[cnt++] = d[u] ^ edge[i].d ^ d[v];
        else dfs(v, d[u] ^ edge[i].d);
    }
}
struct LinearBasis {
    LL a[maxl+1];
    LinearBasis() { memset(a, 0, sizeof a); }
    void insert(LL t) {
        for (int i = maxl; i >= 0; --i) {
            if (!(t >> i & 1)) continue;
            if (a[i]) t ^= a[i];
            else {
                for (int j = 0; j < i; ++j) if (t >> j & 1) t ^= a[j];
                for (int j = i+1; j <= maxl; ++j) if (a[j] >> i & 1) a[j] ^= t;
                a[i] = t;
                return;
            }
        }
    }
    LL condMax(LL t) {
        LL ret = t;
        for (int i = maxl; i >= 0; --i) {
            if (!(a[i] >> i & 1)) continue;
            if (ret >> i & 1) continue;
            ret ^= a[i];
        }
        return ret;
    }
};
int main() {
    int n, m;
    scanf("%d%d", &n, &m);

    tot = 0; memset(ne, -1, sizeof ne);
    for (int i = 0; i < m; ++i) {
        int u, v; LL d;
        scanf("%d%d%lld", &u, &v, &d);
        add(u, v, d);
    }
    dfs(1, 0);

    LinearBasis lb;
    for (int i = 0; i < cnt; ++i) {
        if (b[i]) lb.insert(b[i]);
    }

    printf("%lld\n", lb.condMax(d[n]));

    return 0;
}

posted @   救命怀  阅读(357)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示