题目描述
给出一个 N 个点的带权无向图,要求从 1 号点到 N 号点的一条路径,使得路径上的边 权异或值最大.
输入格式
第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。
接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。
输出格式
仅包含一个整数,表示最大的XOR和(十进制结果) 。
样例
样例输入
5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2
样例输出
6
数据范围与提示
【题目来源】
WC2011
solution
如果没有环,那么方案是确定的。
对于每一个环我们都可以选择走或不走。
那么这就是一个线性基的问题了。
稍稍回忆一下线性基。
问题:给一个集合,求异或和最大的一个子集。
我们考虑一个60位的数组。
现在一个个往里加数。
枚举这个数从高位到低位的所有1,如果线性基这一位为0,那么就加入,然后break;
否则异或上这位上的数继续取。
这样子原集合中能异或出的所有数新集合中也能异或出来。
然后从高位到低位贪心即可。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define maxn 100005 #define ll long long using namespace std; int n,m,head[maxn],tot=1,top; ll t3,c[maxn*5],d[maxn],b[63]; bool vis[maxn]; struct node{ int v,nex; ll w; }e[maxn*2]; void lj(int t1,int t2,ll t3){ e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot; } void dfs(int k,ll v){ vis[k]=1;d[k]=v; for(int i=head[k];i;i=e[i].nex){ if(!vis[e[i].v]){ dfs(e[i].v,v^e[i].w); } else c[++top]=v^d[e[i].v]^e[i].w; } } int main() { cin>>n>>m; for(int i=1,t1,t2;i<=m;i++){ scanf("%d%d%lld",&t1,&t2,&t3); lj(t1,t2,t3);lj(t2,t1,t3); } dfs(1,0); for(int i=1;i<=top;i++){ for(int j=62;j>=0;j--){ if(c[i]&(1LL<<j)){ if(!b[j]){b[j]=c[i];break;} else c[i]^=b[j]; } } } ll ans=d[n]; for(int i=62;i>=0;i--){ ans=max(ans,ans^b[i]); } cout<<ans<<endl; return 0; }