洛谷·[WC2011]最大XOR和路徑

初見安~這裏是傳送門:洛谷P4151 最大XOR路徑

題解:

題意很簡單,求一無向圖從1到n的路徑的最大異或和,可以重複走邊和點。

衆所周知,無向圖找路徑,遇事不決找連通分量或者環【什麼東西啊!但有的時候真的可以撞對的】。這個題的話連通分量似乎跟路徑關係不大,所以我們找環吧。環有個很優美的性質就是可以走一圈後回到原點,繼續走別的路徑,並且會獲得整個環上的邊權的異或和。

我們假定當前已有鏈路徑1~n,外面有一個環:

如果我們選擇去走環,去環裏繞一圈再回來,那麼x邊和y邊分別走了兩次,異或起來貢獻爲0。換言之,我們不需要考慮從路徑連接到環的邊

所以找一條從1到n的路徑,然後看再去走哪些環最優就可以了。後半句我們可以預處理所有環的異或和,扔進線性基【線性基基本操作】,前半句的話其實隨便找一條路徑就可以了。因爲假設存在兩條從1到n的鏈路徑,那麼一定會形成一個大環。這時候如果你選的不是最優的那一條路,那麼異或上大環過後就相當於走最優的那條路了。

好了上代碼——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 50005
#define maxm 100005
//#define int long long
using namespace std;
typedef long long ll;
ll read() {
	ll x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

struct edge {ll to, w, nxt;} e[maxm << 1];
int head[maxn], k = 0;
void add(ll u, ll v, ll w) {e[k] = {v, w, head[u]}; head[u] = k++;}

int n, m, tot = 0, dfn[maxn];
bool vis[maxn];
ll p[maxn], dis[maxn], rand_dis;
void insert(ll x) {//加入線性基
	for(ll i = 63; i >= 0; i--) if(x >> i) {
		if(!p[i]) {p[i] = x; return;}
		x ^= p[i];
	}
}

void dfs(int u) {
	vis[u] = true; dfn[u] = ++tot;//dfn是時間戳,可以用來小小優化一下避免一個環被多次找到
	for(int i = head[u], v; ~i; i = e[i].nxt) {
		v = e[i].to; 
		if(!vis[v]) dis[v] = dis[u] ^ e[i].w, dfs(v);
		else if(dfn[u] > dfn[v]) insert(dis[u] ^ e[i].w ^ dis[v]);
	}
}

signed main() {
	memset(head, -1, sizeof head);
	n = read(), m = read();
	for(ll u, v, w, i = 1; i <= m; i++) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
	dfs(1); rand_dis = dis[n];
	for(int i = 63; i >= 0; i--) rand_dis = max(rand_dis, rand_dis ^ p[i]);//直接找最大
	printf("%lld\n", rand_dis);
	return 0;
}

關於裏面的那個dfn,其實手枚可以發現一個環可能被多次扔進去的。所以加一個dfn可以稍稍優化一點,總時間快了100ms的樣子。

迎評:)
——End——

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章