题目描述
输入
第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。
输出
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。
样例输入
5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2
样例输出
6
提示
写这题必须了解异或的这个性质c^b^c = b;
因为题目是存在环的,所以我们将每个环的异或和用a[i]表示,用的dfs抛出每个环上的异或值,由于c^b^c = b的性质,我们可以知道,从起点出发经过一个环后回到起点时,所得的异或和为该环的异或和。所以我们可以求出a的线性基,在得到任意一条1到n路径上的异或和便可得到异或和的最大值。
下面上代码
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stdio.h>
#include <map>
#include<ctype.h>
using namespace std;
typedef long long LL;
const int MAX_BASE = 60;
const int MAX = 200000 + 50;
LL a[MAX]; // 用来存每个环的异或和 ,注意a数组开大一点,因为不确定有几个环
LL b[MAX_BASE + 5]; // 储存线性基
int head[MAX];
int vis[MAX];
int cnt = 0; // 记录环的个数
int k = 0; // 记录边的条数
LL d[MAX];
struct Edge
{
int to;
int next;
LL w;
}edge[MAX];
void add(int a, int b, LL c){ //前向星建图
edge[k].to = b;
edge[k].next = head[a];
edge[k].w = c;
head[a] = k++;
}
void dfs(int rt){
vis[rt] = 1;
for(int e = head[rt]; e != -1; e = edge[e].next){
int v = edge[e].to;
if(v == rt){
continue;
}
LL w = edge[e].w;
if(vis[v]){ // 走到环的起点
a[cnt++] = d[v] ^ d[rt] ^ w; //根据c ^ b ^c = b 的性质,此处需对d[v]异或,便可得到该环的异或和
} else{
d[v] = d[rt] ^ w; //记录的到1到v的异或和(不唯一,但只记录一种情况)
dfs(v);
}
}
}
void prepare() { //建立所有环异或和的线性基
memset(b, 0, sizeof b);
for (int i = 0; i < cnt; ++i){
for (int j = MAX_BASE; j >= 0; --j){
if (a[i] >> j & 1) {
if (b[j]) a[i] ^= b[j];
else {
b[j] = a[i], cnt++;
for (int k = j - 1; k >= 0; --k) if (b[k] && ((b[j] >> k) & 1)) b[j] ^= b[k];
for (int k = j + 1; k <= MAX_BASE; ++k) if ((b[k] >> j) & 1) b[k] ^= b[j];
break;
}
}
}
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(head, -1, sizeof(head));
for(int i = 0; i < m; i++){
int a, b;
LL c;
scanf("%d%d%lld", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
dfs(1);
prepare();
LL ans = d[n];
for(int i = 60; i >= 0; i--){ // 得到结果
if((ans ^ b[i]) > ans){
ans ^= b[i];
}
}
printf("%lld\n", ans);
return 0;
}