Educational Codeforces Round 27 G.Shortest Path Problem? 线性基、dfs
题目链接
题目大意
给定一张 n n n ( 1 ≤ \leq ≤ n n n ≤ \leq ≤ 1 0 5 10^{5} 105) 个点 m m m ( n − 1 n-1 n−1 ≤ \leq ≤ m m m ≤ \leq ≤ 1 0 5 10^{5} 105) 条边的无向图,一开始你在点 1,且价值为 0,每次你可以选择一个相邻的点,然后走过去,并将价值异或上该边权,如果在点 n n n,你可以选择结束游戏,求一种方案,使得结束游戏后价值最小。
思路
所求的最短 x o r xor xor 路径一定是由一条链加 0 0 0 个或多个环构成。
首先,这一条链任选即可,因为该图是连通的,若所选的链 A A A 非最佳的链 B B B ,由于它们所处的图是连通的, 1 1 1 到 n n n 之间有多条路径说明其中形成了环,用路径 A A A x o r xor xor 上该环一定能得到最佳路径 B B B,这样就选出了最佳链。至于要不要有环,只需要贪心一下,要是 x o r xor xor 上该环能使得 x o r xor xor 减小就异或上它。
其次,要预处理出所有的环,可直接从1号点开始 d f s dfs dfs ,每经过一个点就记录从1号点到当前点的路径 x o r xor xor 和。若一个点被多次经过则说明成环了,存下该环的路径异或和,用线性基存。
code
#include <bits/stdc++.h>
#define int long long
#define ll long long
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 1000;
vector<pii> v[N];
int w[N], p[N], a[35];
bool st[N];
int n, m;
void insert(int x)
{
for (int i = 30; i >= 0; --i)
{
if (!((1ll << i) & x))
continue;
if (a[i])
x ^= a[i];
else
{
a[i] = x;
break;
}
}
}
void dfs(int id, int fa)
{
st[id] = 1;
for (auto k : v[id])
{
if (k.first == fa)
continue;
if (st[k.first])
{
int tmp = p[id] ^ p[k.first] ^ w[k.second];
insert(tmp);
}
else
{
p[k.first] = p[id] ^ w[k.second];
dfs(k.first, id);
}
}
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i)
{
int a, b, c;
cin >> a >> b >> c;
v[a].push_back({b, i});
v[b].push_back({a, i});
w[i] = c;
}
dfs(1, -1);
int res = p[n];
for (int i = 30; i >= 0; --i)
if ((res ^ a[i]) < res)
res = res ^ a[i];
cout << res << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}