2115: [Wc2011] Xor

2115: [Wc2011] Xor

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 2568   Solved: 1086
[ Submit][ Status][ Discuss]

Description

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

HINT

Source

[ Submit][ Status][ Discuss]

学习了一个叫做xor线性基的算法
首先,,要有一点点高等代数的知识(了解线性空间,以及什么是基。。)
因为这个路径是xor出来的,那么最大路一定是由一条主路径xor一堆奇怪的环
对于原路径存在的一条环,如果我们想用它的权值,直接xor上就行
因为可以从起点走到环绕一圈再回起点,保留下来的只有环上的权值
首先任意选出一条从1到n的路径,然后贪心地选择一些环xor上去,最终就能得到最大值
这样一定是正确的。。因为最大路径总是能通过这样的xor出来
这大概是利用了xor同一个数两次等于没有xor过的性质--
通过对环的权值的xor把真正的最大路搞出来
那么什么样的环能选?
首先对于环的个数,,,

借用Mektpoy神犇的图。。
这是一棵对于无向图的dfs树,红边一定是不存在的
若红遍存在那么dfs序将被改写
那么对于绿边(返祖边)一条边恰好对应一个环,于是环的个数是O(m)的
这样我们就找出了所有环
把它们从大到小排好
假设我们处理的边权是60位二进制数。。
从高位到低位逐位处理
从第一个数开始,若找到一个当前位为1的数,那么把后面的数都高斯消元一遍
还是因为xor的特殊性,,消元找基一定是正确的
如此重复60次,这60次里每次选择的数就是最后我们要保留的基
贪心地选择基就好了
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E5 + 10;
typedef long long LL;

struct E{
	int to,num; LL va;
	E(){}
	E(int to,int num,LL va): to(to),num(num),va(va){}
};

int n,m,tot;
LL w[maxn],A[maxn],B[66],ans;
bool bo[maxn],vis[maxn];

vector <E> v[maxn];

bool cmp(const LL &x,const LL &y) {return x > y;}
void Dfs(int x,int fa)
{
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i].to;
		if (to == fa) continue;
		if (vis[to]) {
			if (bo[v[x][i].num]) continue;
			A[++tot] = (w[x]^w[to]^v[x][i].va);
			bo[v[x][i].num] = 1;
			continue;
		}
		vis[to] = 1;
		w[to] = (v[x][i].va^w[x]);
		Dfs(to,x);
	}
}

LL getLL()
{
	LL ret = 0;
	char ch = getchar();
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = 10LL*ret + 1LL*(ch - '0'),ch = getchar();
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	n = getLL();
	m = getLL();
	for (int i = 1; i <= m; i++) {
		int x,y; LL w;
		x = getLL();
		y = getLL();
		w = getLL();
		v[x].push_back(E(y,i,w));
		v[y].push_back(E(x,i,w));
	}
	vis[1] = 1; Dfs(1,0); ans = w[n];
	if (!vis[n]) {cout << 0 << endl; return 0;}
	sort(A + 1,A + tot + 1,cmp);
	int las = 1;
	for (LL j = 60; j >= 0; j--) 
		for (int i = las; i <= tot; i++)
			if (A[i]&(1LL<<j)) {
				for (int k = i + 1; k <= tot; k++)
					if (A[k]&(1LL<<j))
						A[k] ^= A[i];
				B[j] = A[i];
				las = i + 1;
			}
	for (int i = 60; i >= 0; i--)
		ans = (ans^B[i]) > ans?(ans^B[i]):ans;
	cout << ans << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值