题意:给定一个 n n n个点, m m m条边的无向图,求出一条 1 − > n 1->n 1−>n节点的路径,使得路径上经过的边的权值的异或和最大。
思路:
解决此题只需要基于一个事实:
任意一条
1
−
>
n
1->n
1−>n的路径的异或和都可以由任意一条
1
−
>
n
1->n
1−>n的路径和与图中一些环的异或和组合而成。
为什么呢?
简单画个图,举个例子~:
考虑下面的这个图, n = 8 m = 9 n=8 \ m = 9 n=8 m=9,存在 3 3 3个环,标号 1 , 2 , 3 1,2,3 1,2,3
可见从
1
−
>
8
1->8
1−>8有3条路径。
对于任意的一条,比如说:
1
−
>
5
−
>
6
−
>
7
−
>
8
1->5->6->7->8
1−>5−>6−>7−>8
当该路径异或上标号
2
2
2这个环:
1
−
>
5
−
>
6
−
>
7
−
>
4
−
>
2
−
>
1
1->5->6->7->4->2->1
1−>5−>6−>7−>4−>2−>1
易发现,路径
1
−
>
5
−
>
6
−
>
7
1->5->6->7
1−>5−>6−>7异或上了两次,故直接抵消,剩下的路径异或为:
1
−
>
2
−
>
4
−
>
7
−
>
8
1->2->4->7->8
1−>2−>4−>7−>8
刚好为第二条路径。
同理,当该路径异或上标号 3 3 3这个环,就得到了最下面的 1 − > n 1->n 1−>n的路径。
故易发现:对于一个经过某路径的环来说,一定是从某个点 u u u出发形成两条路径,最后两条路径又一定会在一个点 v v v相聚,故异或上一个环,就相当于对于 u − > v u->v u−>v这个过程,走另外的一条路。
那没有经过路径的环呢?比如
1
−
>
5
−
>
6
−
>
7
−
>
8
1->5->6->7->8
1−>5−>6−>7−>8和环
1
1
1。
对于这种情况,我们可以看成我们先到那个环走上一圈,再原路返回,这样从起点到环的路径走了两次,异或后为
0
0
0,剩下的异或和就只剩环的异或和。
对于上面的情况,异或上环 1 1 1就相当于我们从 1 1 1出发,绕环 1 1 1一圈,回到 1 1 1之后再走路径 1 − > 5 − > 6 − > 7 − > 8 1->5->6->7->8 1−>5−>6−>7−>8到达终点。
故我们可以找出图中的所有的环的异或和,构建其异或线性基,然后对于一条任意的从 1 − > n 1->n 1−>n的路径异或和 d i s [ n ] dis[n] dis[n],从大到小枚举线性基,若能使 d i s [ n ] dis[n] dis[n]增大,则加上其贡献。
至此,大功告成。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int A = 5e4 + 10;
class Gra{
public:
int v,next;ll w;
}G[A<<2];
int head[A],tot,twt,n,m;
ll dis[A],a[A<<2],b[110];
bool vis[A];
void add(int u,int v,ll w){
G[tot].v = v;
G[tot].w = w;
G[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u,int pre){
vis[u] = 1;
for(int i=head[u] ;i!=-1 ;i=G[i].next){
int v = G[i].v;ll w = G[i].w;
if(v == pre) continue;
if(!vis[v]){
dis[v] = dis[u] ^ w;
dfs(v,u);
}
else a[++twt] = dis[u]^dis[v]^w;
}
}
void init_Xor(){
for(int i=1 ;i<=twt ;i++) for(int j=62 ;j>=0 ;j--){
if(a[i]>>j & 1){
if(b[j]) a[i] ^= b[j];
else{
b[j] = a[i];
for(int k=j-1 ;k>=0 ;k--) if(b[k] && (b[j]>>k&1)) b[j] ^= b[k];
for(int k=j+1 ;k<=62;k++) if(b[k]>>j&1) b[k] ^= b[j];
break;
}
}
}
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1 ;i<=m ;i++){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs(1,1);init_Xor();
for(int i=62 ;i>=0 ;i--) if((dis[n]^b[i]) > dis[n]) dis[n]^=b[i];
printf("%lld\n",dis[n]);
return 0;
}