[WC2011]最大XOR和路径 题解

[WC2011]最大XOR和路径 题解

题目地址:洛谷:【P4151】[WC2011]最大XOR和路径 – 洛谷、BZOJ:Problem 2115. — [Wc2011] Xor

题目描述

WC2011xor

题意简述

给你一个n点m边的无向图,求一条1到n的路径使边权异或和最大。

输入输出格式

输入格式:
第一行包含两个整数 N 和 M, 表示该无向图中点的数目与边的数目。
接下来 M 行描述 M 条边,每行三个整数Si,Ti,Di,表示 Si 与 Ti 之间存在一条权值为 Di 的无向边。
图中可能有重边或自环。

输出格式:
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。

输入输出样例

输入样例#1:

5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2 

输出样例#1:

6

说明

WC2011xor

题解

首先,我们考虑异或和最大的路径应该是怎么样的。我们考虑图中的环,显然我们可以从任意一个节点走到环上,绕环一圈,再走回来,这样只有这个环的权值异或和就被异或了进去。那么我们可以DFS处理一遍环的权值异或和来得到一个集合。
具体来说,DFS如果遇到一条边两端的点都已经访问过,那么就可以将dis[u]^dis[v]^边权记作环的权值异或和,这是由于dis[u]^dis[v]会将不在环上的边通过一次异或消去。
我们发现,我们只需要用一条1到n的路径再组合上这个集合的某一些权值使得异或和最大就行了。对于这个集合的处理,我们可以求线性基,这样就可以代表所有可能的情况的异或值。我们只需要任选一条1到n的路径并且使用线性基计算最大值就可以了。
任选一条路径为什么是正确的?我们考虑1到n的路径假如有2条,那么这两条路径会构成环,任选其中一条,再异或上这个环,实际上得到了另一条路径的异或和,而环的情况刚刚已经处理过,我们可以保证答案最优。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>

#include <algorithm>
#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 50005;

int n, m;

struct Edge {
    int to;
    LL w;
};

std::vector<Edge> gra[MAXN];

bool vis[MAXN];
LL dis[MAXN];
std::vector<LL> cir;

inline void dfs(int u) {
    vis[u] = true;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i].to;
        if(vis[v]) {
            cir.push_back(dis[v] ^ dis[u] ^ gra[u][i].w);
        } else {
            dis[v] = dis[u] ^ gra[u][i].w;
            dfs(v);
        }
    }
}

LL mat[65];

int main() {
    n = readint(); m = readint();
    int u, v; LL w;
    for(int i = 1; i <= m; i++) {
        u = readint(); v = readint(); w = readint();
        gra[u].push_back(Edge {v, w});
        gra[v].push_back(Edge {u, w});
    }
    dfs(1);
    for(int i = 0; i < cir.size(); i++) {
        for(int j = 63; j >= 0; j--) {
            if(cir[i] & (1ll << j)) { // 这里不是1ll会爆int
                if(!mat[j]) {
                    mat[j] = cir[i];
                    break;
                } else {
                    cir[i] ^= mat[j];
                }
            }
        }
    }
    LL ans = dis[n];
    for(int i = 63; i >= 0; i--) {
        ans = std::max(ans, ans ^ mat[i]);
    }
    printf("%lld", ans);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据