2115: [Wc2011] Xor
Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 6193 Solved: 2631
[Submit][Status][Discuss]
Description
Input
第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。
Output
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。
Sample Input
5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2
Sample Output
6
HINT
题意:
给了N个点,M条边。问从顶点1到顶点N的路径的异或和最大是多少?
思路:
刚学了线性基赶紧试一下这道题。
我们可以先找到任意一条路径从1走到N的,不用管异或值是否是最大,找到了就行。然后在寻找的过程中处理出各个环的异或值。然后把找到的环的值的序列,跑出这个序列的线性基,然后用刚刚找到的任意路径的异或值对线性基里面的值异或求最大值就行了。
为什么要这么做???
因为假设有另一条路径的异或值比我们找到的要大,那么这两条路径肯定就构成一个环,既然成环了,那么这个环的信息肯定也在线性基里能体现,而用ans作初值与线性基里的值求异或最大的过程中,就是在优化路径的过程使得路径异或值能最大。
还有一个问题?既然刚才分析成环了,为什么不把这个值扔进线性基里,然后再跑个线性基最大值呢?因为ans作初值的时候,能保证这一定是一条从1到N的路径,而如果扔进线性基里求了最大值,则并不能保证这一点!
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int cnt,tot;
int head[maxn*2];
int vis[maxn*2];
ll dis[maxn*2];
ll circle[maxn*2];
ll a[62];
struct Edge
{
int y,next;
ll w;
}edge[maxn*2];
void add_edge(int x,int y,ll w)
{
++cnt;
edge[cnt].w=w;
edge[cnt].y=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
void dfs(int u)
{
vis[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].y;
if(!vis[v]) dis[v]=dis[u]^edge[i].w,dfs(v);
else circle[++tot]=dis[v]^dis[u]^edge[i].w;
}
}
void ins(ll x)
{
for(int i=60;i>=0;i--)
{
if(x>>i&1)
{
if(!a[i])
{
a[i]=x;
break;
}
else x^=a[i];
}
}
}
int main()
{
int n,m,x,y;
ll w;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld",&x,&y,&w);
add_edge(x,y,w);
add_edge(y,x,w);
}
dfs(1);
for(int i=1;i<=tot;i++)
ins(circle[i]);
ll ans=dis[n];
for(int i=60;i>=0;i--)
ans=max(ans,ans^a[i]);
printf("%lld\n",ans);
return 0;
}