[WC2011]最大XOR和路径

[WC2011]最大XOR和路径

给出一有n个点m条变的无向联通图,定义路径长度为路径上的边权的异或和,询问从起点1到终点n的最大路径长度,(n≤50000,m≤100000)

注意到异或和问题,即使已经可以向线性基靠了,之所以为何会推出一下结论,笔者猜测是把线性空间与图论对比,而有考虑到图论中最重要的环,在加上对链的考虑,因为你必须要有一条链从起点达到终点。

先介绍一下结论

  1. 从任意一个点出发到达任意一个点原路返回没有影响,这个显然,因为异或把中间的路径异或掉了,可以理解成瞬间移动,移动到一个点,做了一些事情,再返回,忽略中间过程。

  2. 进入一个环,不走完等于没走,显然根据结论1,原路返回等于没走,于是联系结论1,易知,瞬间移动的目的在于进入一个环,收取边权,于是不难得知问题关键在于环。

  3. 对于还套环,我们只能走完,只能走其中一个子环,如图,显然从任何一个地方进来,再回到原点出去,必然是其中一个子环,而把一些子环的路径长度加入线性基,必然也可以通过这些子环异或出其他的子环的路径长度,即环的类异或性。

  4. 对于任意一条确定了起点和终点的路径,它的路径变换可以通过与环的异或改变,如图中路径1->2->3,异或上最大的环就是1->4->3。

总上这些结论,我们只要dfs把所有的环加入线性基,而选择一条必须选的链与线性基求最大异或和,而求最大异或和的过程包含了对路径的改变,以及瞬间移动获取环上的边权和还套环中环与环的选择,于是可以证明这种做法的正确性。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define ll long long
#define Size 60000
using namespace std;
struct base{
	ll b[64];
	il void insert(ll x){
		for(ri int i(63);i>=0;--i)
			if(x>>i){
				if(b[i])x^=b[i];
				else return(void)(b[i]=x);
			}
	}
	il ll ask(ll x){
		for(ri int i(63);i>=0;--i)
			if((x^b[i])>x)x^=b[i];
		return x;
	}
}B;
struct point{
	point*next;int to;ll w;
}*head[Size];
ll dis[Size];
bool check[Size];
void dfs(int);
il void link(int,int,ll);
template<class free>
il void read(free&);
int main(){
	int n,m,u,v;ll w;read(n),read(m);
	while(m--)read(u),read(v),read(w),
				  link(u,v,w),link(v,u,w);
	dfs(1),printf("%lld",B.ask(dis[n]));
	return 0;
}
void dfs(int x){
	check[x]|=true;
	for(point *i(head[x]);i!=NULL;i=i->next)
		if(check[i->to])B.insert(dis[i->to]^dis[x]^i->w);
		else dis[i->to]=dis[x]^i->w,dfs(i->to);
}
il void link(int u,int v,ll w){
	head[u]=new point{head[u],v,w};
}
template<class free>
il void read(free &x){
	x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
原文地址:https://www.cnblogs.com/a1b3c7d9/p/10874642.html