[WC2011]最大XOR和路径

题目

题目描述
XOR(异或)是一种二元逻辑运算,其运算结果当且仅当两个输入的布尔值不相等时才为真,否则为假。 XOR 运算的真值表如下(11 表示真, 00 表示假):

QQ20180128145629.png

而两个非负整数的 XOR 是指将它们表示成二进制数,再在对应的二进制位进行 XOR 运算。

譬如 1212 XOR 99 的计算过程如下:

QQ20180128145728.png

故 1212 XOR 9 = 59=5。

容易验证, XOR 运算满足交换律与结合律,故计算若干个数的 XOR 时,不同的计算顺序不会对运算结果造成影响。从而,可以定义 KK 个非负整数 A_1A
1

,A_2A
2

,……,A_{K-1}A
K−1

,A_KA
K

的 XOR 和为

A_1A
1

XOR A_2A
2

XOR …… XOR A_{K-1}A
K−1

XOR A_KA
K

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

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

输入格式
输入文件 xor.in 的第一行包含两个整数 NN 和 MM, 表示该无向图中点的数目与边的数目。

接下来 MM 行描述 MM 条边,每行三个整数 S_iS
i

, T_iT
i

, D_iD
i

, 表示 S_iS
i

与 T_iT
i

之间存在一条权值为 D_iD
i

的无向边。

图中可能有重边或自环。

输出格式
输出文件 xor.out 仅包含一个整数,表示最大的 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
说明/提示
【样例说明】

QQ20180128150132.png

如图,路径1 \rightarrow 2 \rightarrow 4 \rightarrow 3 \rightarrow 5 \rightarrow 2 \rightarrow 4 \rightarrow 51→2→4→3→5→2→4→5对应的XOR和为

22 XOR 11 XOR 22 XOR 44 XOR 11 XOR 11 XOR 3 = 63=6

当然,一条边数更少的路径1 \rightarrow 3 \rightarrow 51→3→5对应的XOR和也是22 XOR 4 = 64=6。

【数据规模】

对于 20 %20% 的数据,N \leq 100N≤100, M \leq 1000M≤1000,D_i \leq 10^{4}D
i

≤10
4

对于 50 %50% 的数据,N \leq 1000N≤1000, M \leq 10000M≤10000,D_i \leq 10^{18}D
i

≤10
18

对于 70 %70% 的数据,N \leq 5000N≤5000, M \leq 50000M≤50000,D_i \leq 10^{18}D
i

≤10
18

对于 100 %100% 的数据,N \leq 50000N≤50000, M \leq 100000M≤100000,D_i \leq 10^{18}D
i

≤10
18

思路

发现答案肯定是由随便一条由 1 到 n 的路径异或上图中若干个环。
因为你回到一号点,非环的点都被亦或抵消了
若干个数异或最大值交给线性基去做就好了,这里面相当于指定了初始值,但是不影响,只是不需要优化线性基构造,因为优化了也不能保证和初始值之间互不影响。

代码

#include<bits/stdc++.h>
#pragma GCC optimize(3)
#define ll long long
using namespace std;
const int N=5e4+77,M=N<<2;
int n,m,hd[N],to[M],nt[M],pn;
ll ans,xr[N],d[64],w[M];
bool vs[N];
void ins(ll x)
{
	for(int i=62;i>=0;--i)
		if(1&(x>>i))
			if(d[i])x^=d[i];
			else{d[i]=x;return;}
}
void dfs(int x,int fr,ll res)
{
	vs[x]=1,xr[x]=res;
	for(int i=hd[x],v; i; i=nt[i])
	{
		v=to[i];if(v==fr)continue;
		if(!vs[v])dfs(v,x,res^w[i]);
		else ins(res^w[i]^xr[v]);
	}
}
int main()
{
	scanf("%d%d",&n,&m);ll z;
	for(int i=1,u,v;i<=m;++i)
		scanf("%d%d%lld",&u,&v,&z),
		pn++,to[pn]=v,w[pn]=z,
		nt[pn]=hd[u],hd[u]=pn,
		pn++,to[pn]=u,w[pn]=z,
		nt[pn]=hd[v],hd[v]=pn;
	dfs(1,0,0ll),ans=xr[n];
	for(int i=62;i>=0;--i)
		ans^=(ans^d[i])>ans?d[i]:0;
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值