xor运算的性质: a xor b xor b = a。 同一个数被xor偶数次后的结果为0.
题目要求的是一条从1到n的路径, 于是我们可以将该条路径分解为一条无环的直接路径和这条路径上连接的一部分环的结合, 这样只要求出需要的环就行了。
难道我要把所有环都求出来? (假设给出一个完全图, 想想也觉得是2^n级别的了)
机智为上策, 我们只要求出一部分环, 将这些环的权值算出来, 再利用xor运算的性质, 不就可以表示其它的环了? 如果找到了合适的环, 问题就迎刃而解。
于是问题升级为求环的方法。 对于图中的每一个点x, 记f[x]为从点1到点x的xor路径值, 那么在找到另一个已经搜索过的点后, 可以知道这两点确定一个环, 得到公式v = f[x] ^ f[k] ^ e[i].w, v表示这个环的权值。 环的值已经得到, 问题就转化成为对二进制数求xor和最大的贪心问题。
首先将ans赋为f[n], 开始枚举v中的数值。 若第一次遇到最高位为j的数, 将这个数从集合中提出来, 并将相同位置为1的剩余元素与之异或, 这样保证了结合的稳定性, 因为相当于xor了两次, 没有影响。 最终对提出的数进行贪心, 尽可能的将高位全部赋为1, ans get。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 50000 +10
using namespace std;
typedef long long LL;
struct edge
{
int to, next;
LL w;
}e[4*N];
vector<LL>v;
int n, m, num, p[N], flag[N];
LL ans, f[N], r[70];
void read(int &x)
{
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
x = 10*x + c - '0';
c = getchar();
}
}
void read(LL &x)
{
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
x = 10*x + c - '0';
c = getchar();
}
}
void add(int x, int y, LL z)
{
e[++num].to = y;
e[num].w = z;
e[num].next = p[x];
p[x] = num;
}
void init()
{
LL z;
int x, y;
num = 1;
read(n), read(m);
for (int i = 1; i <= m; ++i)
{
read(x), read(y), read(z);
add(x, y, z);
add(y, x, z);
}
}
void dfs(int x, int last)
{
flag[x] = 1;
for (int i = p[x]; i; i = e[i].next)
if (i != last)
{
int k = e[i].to;
if (!flag[k])
{
f[k] = f[x] ^ e[i].w;
dfs(k, i^1);
}
else v.push_back(f[x]^f[k]^e[i].w);
}
}
void deal()
{
dfs(1, 0);
ans = f[n];
int sz = v.size();
for (int i = 0; i < sz; ++i)
for (int j = 63; j >= 0; j--)
if ((v[i]>>j) & 1)
{
if (!r[j])
{
r[j] = v[i];
break;
}
v[i] ^= r[j];
}
for (int i = 62; i >= 0; i--)
if (!((ans>>i)&1) && r[i]) ans ^= r[i];
printf("%lld\n", ans);
}
int main()
{
init();
deal();
return 0;
}