bzoj2115 Xor

题目链接

problem

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

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

solution

考虑先确定一条从\(1\)\(n\)的不含环的路径。

enter description here

如图,我们先选择\(1-2-3-4-9-10\)这条路径,然后发现,我们在3这个位置可以通过\(3-5\)这条边,去环里面转一圈,然后再回来。因为\(3-5\)这条边走了两边。所以增加的边权就是这个环的异或和。

这个我们就可以猜想了。我们先选择任意一个可以从\(1-n\)的路径,称之为链。然后该图中所有的环都是可以产生贡献的。所以我们把这条链的异或和和各个环的异或和全部扔到线性基里去。然后求最大值就行了。

为什么这条链可以任意选呢。因为如果有其他的路径可以到达\(n\),必定与该链形成了环。只要选择那个环就相当于走了另外一条路径。

如图

enter description here

我们可以选择以\(1-2-3-4-9-10\)或者是\(1-2-3-4-11-12-13-9-10\)作为链。
不管以哪一条为链只要异或上这个蓝色的环。就会变成另外一条链。

code

/*
* @Author: wxyww
* @Date:   2019-07-23 14:59:35
* @Last Modified time: 2019-07-23 15:09:13
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 100100;
#define int ll
ll read() {
	ll x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node {
	int v,nxt;ll w;
}e[N << 1];
int head[N],ejs;
void add(int u,int v,int w) {
	e[++ejs].v = v;e[ejs].w = w;e[ejs].nxt = head[u];head[u] = ejs;
}
int vis[N];
ll dis[N],p[N];
void ins(ll x) {
	for(int i = 63;i >= 0;--i) {
		if(!(x >> i & 1)) continue;
		if(!p[i]) {
			p[i] = x;break;
		}
		else x ^= p[i];
	}
}
ll query(ll x) {
	ll ans = x;
	for(int i = 63;i >= 0;--i) if((ans ^ p[i]) > ans) ans ^= p[i];
	return ans;
}
void dfs(int u,int father) {
	vis[u] = 1;;
	for(int i = head[u];i;i = e[i].nxt) {
		int v = e[i].v;
		if(v == father) continue;
		if(vis[v]) {
			ins(dis[u] ^ e[i].w ^ dis[v]);continue;
		}
		else dis[v] = dis[u] ^ e[i].w,dfs(v,u);
	}
}

 main() {
	int n = read(),m = read();
	for(int i = 1;i <= m;++i) {
		int u = read(),v = read();ll w = read();
		add(u,v,w);add(v,u,w);
	}
	dfs(1,0);
	cout<<query(dis[n]);
	return 0;
}
posted @ 2019-07-23 16:46  wxyww  阅读(...)  评论(...编辑  收藏