初見安~這裏是傳送門:洛谷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——