Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2115
Algorithm:
此题一看到是求异或和最大问题的,立即想到使用线性基解题
最终结果发现是由任意一条1~N的路径和若干个环构成的
证明:
1、如果答案中有环不在任意选取的路径上,可以先走到环再走回来
由于异或的自反性,相当于只增加了环的异或和
2、如果答案中的1~N的路径不是这条,那么这条路径一定和当前任意选取的路径形成一个环
那么我们只要再增加这个环上的异或和,就相当于“更改路径”了
那么接下来,我们只要dfs找到所有的环并记录其异或和
选取任意一条1~N的路径作为初始值,和所有环形成的线性基贪心加合即可
Code:
#include <bits/stdc++.h> using namespace std;
typedef long long ll;
typedef pair<ll,ll> P; inline ll read() //IO优化中的int要改为LL!!!
{
char ch;ll num,f=;
while(!isdigit(ch=getchar())) f|=(ch=='-');
num=ch-'';
while(isdigit(ch=getchar())) num=num*+ch-'';
return f?-num:num;
} #define F first
#define S second const int MAXN=5e4+;
const int MAXM=2e5+;
vector<P> G[MAXN];
int n,m;
bool vis[MAXN];
ll dist[MAXN],base[],cir[MAXM],res,cnt=; void dfs(int x)
{
vis[x]=true;
for(int i=;i<G[x].size();i++) //寻找返祖边
{
P v=G[x][i];
if(!vis[v.F]) dist[v.F]=dist[x]^v.S,dfs(v.F);
else cir[++cnt]=dist[x]^dist[v.F]^v.S;
}
} int main()
{
n=read();m=read();
for(int i=;i<=m;i++)
{
ll x=read(),y=read(),z=read();
G[x].push_back(P(y,z));
G[y].push_back(P(x,z));
}
dfs();res=dist[n]; for(int i=;i<=cnt;i++) //构建线性基
for(int j=;j>=;j--)
{
if(!(cir[i]>>j)) continue;
if(!base[j]){base[j]=cir[i];break;}
cir[i]^=base[j];
} for(int i=;i>=;i--) res=max(res,res^base[i]);
cout << res;
return ;
}
Review:
1、异或和MAX <-----> 线性基
2、解决有环问题时,不一定要找到所有的环
大多时候,只要找到dfs返祖边形成的环即可
此题是因为一个含有多条返祖边形成的环的异或和就等于几个“小环”的异或总和
3、充分利用异或的自反性
求解异或和问题中,环+异或可以实现“换路”、“远程加环”等操作
4、如果res的初始值不为0,在和线性基添加时不可以看到1就添加,MAX更稳妥