BZOJ 2115 [Wc2011] Xor (线性基)

Description

考虑一个边权为非负整数的无向连通图,节点编号为 1 到 N,试求出一条从 1 号节点到 N 号节点的路径,使得路径上经过的边权值的 XOR 和最大。

路径可以重复经过某些点或边,当一条边在路径中出现了很多次时,其权值在计算 XOR 和时也要被计算相应多的次数。

 

Input

第一行包含两个整数 N 和 M ,表示该无向图中点的数目与边的数目。

接下来 M 行描述 M 条边,每行三个整数 Si,Ti ,Di,表示 Si 与 Ti 之间存在一条权值为 Di 的无向边。图中可能有重边或自环。

 

Output

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

 

Sample Input

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

 

Sample Output

6

 

思路

因为是无向图,且允许经过重复点以及重复边,所以我们最终所选择的路径可以看作是从 1 -> n 的一条主路径附带很多的环组成。

记每一个环所贡献的权值为 $a_i$ ,主路径权值为 $dis_n$ ,接下来我们要选择某些 $a_i$ 使得总权值最大。

显然我们可以找出 $a_i$ 所代表的线性空间中的一组线性基,然后根据线性基的性质贪心寻找答案。


接下来我们分析为什么这样的做法可行:

  1. 为什么每一个环可以无消耗的加入主路径:具体我们可以从 1 走到环内的一点,然后绕环走完所有的边再原路返回,因为总的权值是各边的异或和,所以来回走同一段路的消耗是 0 ,即每个环的权值我们都可以直接附加到主路径中。
  2. 主路径如何选取:假设从 1 -> n 存在一条以上的路径,那么这些路径之间一定会存在一个或者多个环,此时我们讨论只有一个环的情况(多个环类似),设该环的权值为 $p$ ,主路径的权值为 $dis$ ,显然另一条路径的权值为 $p\oplus dis$ ,而最终我们贪心寻找答案的时候一定会考虑到另一条路径的长度,因此保证结果依然是最大的。

 

AC 代码

#include <bits/stdc++.h>
#define IO                       \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 10;

struct node {
    int from;
    int to;
    int next;
    LL cost;
} edge[maxn << 1];
int head[maxn], tot;
int n, m, idx;
LL a[maxn], dis[maxn], p[maxn];
void init() {
    memset(head, -1, sizeof(head));
    memset(dis, -1, sizeof(dis));
    tot = idx = 0;
}

void addedge(int u, int v, LL cost) {
    edge[tot].from = u;
    edge[tot].to = v;
    edge[tot].cost = cost;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void initBase() {
    for (int i = 0; i < idx; i++) {
        for (int j = 62; j >= 0; j--) {
            if ((p[i] >> j) & 1) {
                if (a[j] == 0) {
                    a[j] = p[i];
                    break;
                } else {
                    p[i] ^= a[j];
                }
            }
        }
    }
}

void dfs(int x, LL cost) {
    if (dis[x] == -1)
        dis[x] = cost;
    else {
        p[idx++] = dis[x] ^ cost;
        return;
    }
    for (int i = head[x]; i != -1; i = edge[i].next) {
        dfs(edge[i].to, edge[i].cost ^ cost);
    }
}

void solve() {
    dfs(1, 0);
    initBase();
    LL ans = dis[n];
    for (int i = 62; i >= 0; i--)
        ans = max(ans, ans ^ a[i]);
    cout << ans << endl;
}

int main() {
#ifdef LOCAL_IM0QIANQIAN
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#else
    IO;
#endif // LOCAL_IM0QIANQIAN
    init();
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        LL u, v, cost;
        cin >> u >> v >> cost;
        addedge(u, v, cost);
        addedge(v, u, cost);
    }
    solve();
    return 0;
}

我想对千千说~