【ybt金牌导航8-1-4】【luogu P4151】路径最大异或和 / 最大XOR和路径

路径最大异或和 / 最大XOR和路径

题目链接:ybt金牌导航8-1-4 / luogu P4151

题目大意

给你一个无向图,边有边权,要你找一条从 1 到 n 的路径,使得路径上经过的边边权异或起来最大。
如果重复经过边,那边权要异或多次。

思路

首先我们想想没有环怎么搞。
那容易 DP 一下得到 \(dis_i\)(从 \(1\)\(i\) 的路径异或和),那答案就是 \(dis_n\)

但你想到它有环。
那假设这些环都不在 \(1\)\(n\) 的最短路径上。
那我们就可以选择专门去经过这个环或不经过,根据异或有贡献分别是环边的异或和以及 \(0\)

那如果在路径上呢?
在这里插入图片描述
红色是你选的一个路径,然后粉色是在路径上的一个环。
那你把它们异或起来,你就发现,它就变成了这个:
在这里插入图片描述
棕色的那个,就是另一条路径了,那我们可以根据要不要一个这个环来改变要走的路径。

那接着有人会问,可能两个小环会组成大环,你要把所有的环都找出来,不也会超时吗?
那我们再画图来看:
在这里插入图片描述
红色是两个小环,粉色是大环。
那你发现,你把两个小环异或起来,就是你要的大环了。
那我们其实可以通过小环的组合异或,得到所有的环。

那问题就变成了给你一堆数,有一个数一定要选(你找的随便一个从 \(1\)\(n\) 的路径),其它的任选一些(小环),要选出的数的异或值最大。
那就是线性基搞搞就好了。

代码

#include<cstdio>
#define ll long long

using namespace std;

struct node {
	ll x;
	int to, nxt;
}e[200001];
int n, m, le[50001], x, y, KK;
ll z, dis[50001], p[101];
bool in[50001];

void add(int x, int y, ll z) {
	e[++KK] = (node){z, y, le[x]}; le[x] = KK;
	e[++KK] = (node){z, x, le[y]}; le[y] = KK;
}

//线性基
void xxj_add(ll x) {
	for (int i = 60; i >= 0; i--)
		if ((x >> i) & 1) {
			if (!p[i]) {
				p[i] = x;
				break;
			}
			x ^= p[i];
		}
}

//dfs
void dfs(int now, int father) {
	in[now] = 1;
	for (int i = le[now]; i; i = e[i].nxt)
		if (!in[e[i].to]) {//正常跑
			dis[e[i].to] = dis[now] ^ e[i].x;
			dfs(e[i].to, now);
		}
		else {//形成了环
			xxj_add(dis[now] ^ dis[e[i].to] ^ e[i].x);
		}
}

ll get_max(ll re) {//线性基求最大值
	for (int i = 60; i >= 0; i--)
		if (!((re >> i) & 1))
			if (p[i])
				re ^= p[i];
	return re;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %lld", &x, &y, &z);
		add(x, y, z);
	}
	
	dfs(1, 0);
	
	printf("%lld", get_max(dis[n]));
	
	return 0;
}
posted @ 2021-07-05 11:41  あおいSakura  阅读(37)  评论(0编辑  收藏  举报