WC2011 最大路径异或和

WC2011 最大XOR和路径

1 题目

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

    QQ20180128145629.png

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

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

    QQ20180128145728.png

    故 12 XOR 9 = 5。

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

    \(A_1 XOR A_2 XOR …… XOR A_{K-1} XOR A_K\)

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

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

2 分析

  • 本题的关键就是对环的处理。题目告诉我们路径和点是可以重复经过的。 根据异或的性质,一条路来回走等于没有走。

  • 我们发现从1到n的路径上会存在两种环。 如下图所示,n=9。

  • 假设我们从\(1 \rightarrow 2 \rightarrow 3 \rightarrow 5 \rightarrow9\)。 中间我们经过两种环,一个是包含了路径的环,即[2,3,4],还有是和路径无关的环[6,7,8]。为什么[6,7,8]和路径无关呢,因为我们可以把这个环看成是在路径之外额外添加的。边(5,6)由于走了两次,所以相当于不存在。 我们可以通过灌水把和起点1连通的所有环的异或和全部都算出来。

  • 然后我们随便选择一条从起点到终点的路径,假如说就是\(1 \rightarrow 2 \rightarrow 3 \rightarrow 5 \rightarrow 9\),那么我们外加环[6,7,8]就可以得到我们的最优值。有些同学可以会问,如果我们一开始选择的是\(1 \rightarrow 2 \rightarrow 4 \rightarrow 3 \rightarrow 3 \rightarrow 5 \rightarrow 9\),如果这条路路径比原路径更好,那么我们是不是答案就错掉了。其实没关系,因为我们即使选择了\(1 \rightarrow 2 \rightarrow 3 \rightarrow 5 \rightarrow 9\),我们也可以异或环[2,3,4],这就把[2,3]这条边给消去了,添加了[2,4]和[4,3]这两条边。

  • 所以总的思路已经很明显了,我们只要先随便找一条从起点到终点的路,然后用这里的环去异或这个路径。 那么如何才能求得异或的最大值呢?我们可以对所有环求线性基,然后假设当前路径的异或值是V,那么我们从大到小枚举线性基,如果ans异或了当前的d[i]以后可以变大,就异或一下,否则就不要异或。 这个贪心其实很好理解,如果ans是\((010010)_2\),当前的d[i]的最高位如果比ans大,那么我们肯定要异或进去。如果当前的i是4,那么此时ans异或d[4]肯定变小,而且以后再怎么补也补不回来。同理,i比4小的时候也是如此。

  • 总的时间复杂度:\(O(n+m+60\times n)\),前面是灌水的时间复杂度,后面是建立线性基的最坏时间复杂度。

3 代码

#include<bits/stdc++.h>
using namespace std;  
#define N 50005 
#define M 100005  
#define ll long long 
struct edge{
    int to,nt;
    ll w; 
}e[M<<1];  
int h[N],cnt,n,m,vis[N];  
ll t[N],d[70];  
void add(int a,int  b,ll c){
    e[++cnt].to=b; e[cnt].w=c; e[cnt].nt=h[a]; h[a]=cnt;  
}
void ins(ll x){
    for(int i=62;i>=0;i--)
        if(x&(1LL<<i)){
            if(d[i]) x^=d[i]; 
            else {
                d[i]=x; 
                break;  
            }
        }
}
void dfs(int x,ll val){
    t[x]=val;  
    vis[x]=1;  
    for(int i=h[x];i;i=e[i].nt){
        int v=e[i].to;  
        if(!vis[v]) dfs(v,val^e[i].w);  
        else ins(val^e[i].w^t[v]);  
    }
}
int main(){
    scanf("%d%d",&n,&m);  
    while (m--){
        int x,y;
        ll z;  
        scanf("%d%d%lld",&x,&y,&z);  
        add(x,y,z); 
        add(y,x,z); 
    }
    dfs(1,0);
    ll ans=t[n];    
    for(int i=62;i>=0;i--)  
        ans=max(ans,ans^d[i]); 
    printf("%lld\n",ans); 
    return 0;   
}
posted @ 2020-06-11 15:19  zjxxcn  阅读(374)  评论(0编辑  收藏  举报