[BZOJ 2115 Wc2011 Xor]线性基

[BZOJ 2115 Wc2011 Xor]线性基

分类:Math bitmask 线性基

1. 题目链接

[BZOJ 2115 Wc2011 Xor]

2. 题意描述

一个边权非负整数的无向连通图,节点编号为1~n,求出一条从1号节点到n号节点的路径,使得路径上经过的边的权值 XOR .
数据范围: n50000,m100000,01018

3. 解题思路

线性基解决的问题是:给定一个非负整数集合 S ,从中选出最小的子集T,使得对于集合 S 中的任意一个元素,都可以由集合T中若干元素 XOR 而成。
非常类似于线性代数里面的最大线性无关组的定义。

然后就是要知道这样一个性质:图中所有环都是可以被异或的,因为我们可以从 u 出发到环的某一个点t,然后绕环一圈回到点 t ,然后再从t u 原路返回。那么此时的异或值就是环的异或值。
对于这个题目来说,可以在原图中dfs搞出一个生成树,并求出所有环的异或值。对所有环的向量组求一个线性基。那么答案就是生成树上从1 n <script type="math/tex" id="MathJax-Element-2803">n</script>的路径异或线性基中的若干向量的最大值。
那么做法就是从高位往低位贪心地选择加入或者不加入。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int inf = 0x3f3f3f3f;
const ll infl = 0x3f3f3f3f3f3f3f3fLL;
template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }

const int MAXN = 50005;
const int MAXM = 100005;
typedef pair<int, ull> Edge;
vector<Edge> G[MAXN];
int n, m;
ull xpath[MAXN];
vector<ull> xorv;
ull base[100];
bool used[MAXN];

void dfs(int u, int pre) {
    used[u] = true;
    for (int i = 0; i < G[u].size(); ++i) {
        Edge& e = G[u][i];
        if (e.first == pre) continue;
        if (used[e.first]) {
            xorv.push_back(xpath[e.first] ^ xpath[u] ^ e.second);
        } else {
            xpath[e.first] = xpath[u] ^ e.second;
            dfs(e.first, u);
        }
    }
}

void Guass_base() {
    for (int i = 0; i <= 62; ++i) base[i] = 0;
    for (int i = 0; i < xorv.size(); ++i) {
        for (int j = 62; j >= 0; --j) {
            if (!(xorv[i] >> j & 1)) continue;
            if (!base[j]) {
                base[j] = xorv[i];
                break;
            }
            xorv[i] ^= base[j];
        }
    }
}

ull query(ull val) {
    for (int i = 62; i >= 0; --i) umax(val, val ^ base[i]);
    return val;
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    int u, v; ull w;
    while (~scanf("%d %d", &n, &m)) {
        for (int i = 1; i <= n; ++i) {
            G[i].clear();
            used[i] = false;
        }
        xorv.clear();
        for (int i = 1; i <= m; ++i) {
            scanf("%d %d %llu", &u, &v, &w);
            G[u].push_back(Edge(v, w));
            G[v].push_back(Edge(u, w));
        }
        dfs(1, 0);
        sort(xorv.begin(), xorv.end());
        xorv.erase(unique(xorv.begin(), xorv.end()), xorv.end());
        Guass_base();
        ull ans = query(xpath[n]);
        printf("%llu\n", ans);
    }
#ifdef ___LOCAL_WONZY___
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值