1. 程式人生 > 其它 >「WC2011」最大XOR和路徑 做題記錄

「WC2011」最大XOR和路徑 做題記錄

利用了 xor 兩次為 \(0\) 的性質。
假設我們已經有了一條路徑,要將其拓展至一條新的路徑並更新答案,一種可能的拓展是存在一個環與該路徑相交,就能將環上路徑取反。
再仔細想想,其實不與該路徑相交的環也是可以拓展的物件,完全可以從路徑上某個點出發,進入該環,繞一圈,沿相同路徑回來,
這樣做前往環的一部分路徑的貢獻是重複了的,因此為 \(0\)
觀察這兩種拓展,發現每次都可以將原本的路徑異或上任意一個環。
相當於確定了一條路徑之後,可以選擇異或一些環,使異或和最大。可以用線性基搞定。
這條路徑如何選擇?
任意選即可,如果存在兩條不同的路徑,它們至少有兩個點相交,所以一定存在環可以使兩者相拓展。
那麼直接把所有環的異或和扔到線性基裡,加上任意一條 \(1\)

\(n\) 的路徑求一下基可表達的最大值即可。


#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define ull unsigned long long
using namespace std;
const int N=5e4+10, M=1e5+10;
int n,m,tot,last[N];
bool bz[N];
struct edge{
	int st,en,next;ull v;
}E[M<<1];
ull t[100],ans,dis[N];
void add(int x,int y,ull z){
	E[++tot]=(edge){x,y,last[x],z};
	last[x]=tot;
}
void insert(ull x){
	fd(i,62,0)
		if(x&(1ll<<i))x^=t[i];
	if(x){
		fd(i,62,0)
			if(x&(1ll<<i)){
				t[i]=x;
				break;
			}
	}
}
void dfs(int x,int fr){
	bz[x]=1;
	for(int p=last[x];p;p=E[p].next){
		int y=E[p].en;
		if(y==fr)continue;
		if(bz[y]){
			insert(dis[y]^dis[x]^E[p].v);
		}else{
			dis[y]=dis[x]^E[p].v;
			dfs(y,x);
		}
	}
}
int main(){
	freopen("data.in","r",stdin);
	freopen("data.out","w",stdout);
	scanf("%d%d",&n,&m);
	fo(i,1,m){
		int x,y;ull z;
		scanf("%d%d%llu",&x,&y,&z);
		add(x,y,z);add(y,x,z);
	}
	dfs(1,0);
	ans=dis[n];
	fd(i,62,0){
		if(!(ans&(1ll<<i)))ans^=t[i];
	}
	printf("%llu\n",ans);

	return 0;
}