[WC2011]最大XOR和路径

Posted by Dispwnl on June 16, 2018

题目

题目描述

XOR(异或)是一种二元逻辑运算,其运算结果当且仅当两个输入的布尔值不相等时才为真,否则为假。 XOR 运算的真值表如下($1$表示真,$0$表示假):

而两个非负整数的 XOR 是指将它们表示成二进制数,再在对应的二进制位进行 XOR 运算。

譬如$12$XOR$9$的计算过程如下:

故$12$XOR$9$=$5$。

容易验证, XOR 运算满足交换律与结合律,故计算若干个数的 XOR 时,不同的计算顺序不会对运算结果造成影响。从而,可以定义$K$个非负整数 $A_1,A_2,A_3,A_4…A_{K-1},A_K$的 XOR 和为$A_1$XOR$A_2$XOR$A_3…$XOR$A_K$

考虑一个边权为非负整数的无向连通图,节点编号为$1$到$N$,试求出一条从$1$号节点到$N$号节点的路径,使得路径上经过的边的权值的 XOR 和最大。

路径可以重复经过某些点或边,当一条边在路径中出现了多次时,其权值在计算 XOR 和时也要被计算相应多的次数,具体见样例。

输入输出格式

输入格式:

输入文件 xor.in 的第一行包含两个整数$N$和$M$,表示该无向图中点的数目与边的数目。

接下来$M$行描述$M$条边,每行三个整数$S_i,T_i,D_i$,表示$S_i$与$T_i$之间存在一条权值为$D_i$的无向边。

图中可能有重边或自环。

输出格式:

输出文件 xor.out 仅包含一个整数,表示最大的 XOR 和(十进制结果)。

输入输出样例

输入样例#1:

5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2

输出样例#1:

6

说明

【样例说明】

如图,路径$1→2→4→3→5→2→4→5$对应的XOR和为$2$XOR$1$XOR$2$XOR$4$XOR$1$XOR$1$XOR$3=6$

当然,一条边数更少的路径$1→3→5$对应的XOR和也是$2$XOR$4=6$。

【数据规模】

对于$20\%$的数据,$N\leq 100$,$M\leq 1000$,$D_i\leq 10^4$

对于$50\%$的数据,$N\leq 1000$,$M\leq 10000$,$D_i\leq 10^{18}$

对于$70\%$的数据,$N\leq 5000$,$M\leq 50000$,$D_i\leq 10^{18}$

对于$100\%$的数据,$N\leq 50000$,$M\leq 100000$,$D_i\leq 10^{18}$

题解

因为是求XOR最大一下就想到线性基

但是这是在图里该怎么解决呢?

因为XOR的性质,一条边走两次就相当于没走

题目中还有:

路径可以重复经过某些点或边

所以一条路径可以通过经过XOR一堆环成为另一条路径这样子

所以可以找到所有的环和任意一条路径然后用线性基求一下最大值

$dfs$找环,并储存一条路径的XOR值

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# define LL long long
using namespace std;
const int MAX=1e5+1,MAXN=64;
struct p{
    int x,y;
    LL dis;
}c[MAX<<1];
int n,m,num,tot;
int h[MAX];
LL a[MAX<<4],b[MAXN],d[MAX];
bool use[MAX];
void add(LL dis,int x,int y)
{
    c[++num]=(p){h[x],y,dis},h[x]=num;
    c[++num]=(p){h[y],x,dis},h[y]=num;
}
LL read()
{
    LL x=0;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
    return x;
}
void dfs(int x)
{
    for(int i=h[x];i;i=c[i].x)
      if(!use[c[i].y])
      {
      	use[c[i].y]=1,d[c[i].y]=d[x]^c[i].dis;
        dfs(c[i].y);
      }
      else a[++tot]=d[x]^c[i].dis^d[c[i].y];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;++i)
      add(read(),read(),read());
    use[1]=1,dfs(1);
    LL ans=d[n];
    for(int i=1;i<=m;++i)
      for(int j=63;~j;--j)
        if(a[i]&(1ll<<j))
        {
        	if(!b[j])
        	{
        		b[j]=a[i];
        		break;
            }
            else a[i]^=b[j];
        }
    for(int i=63;~i;--i)
      if((ans^b[i])>ans) ans^=b[i];
    return printf("%lld",ans),0;
}