BZOJ2115 [WC2011]最大XOR和路径

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2115

最大XOR和路径

题目描述

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

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

譬如 12 XOR 99 12   X O R   99 的计算过程如下:

12 XOR 9=5 12   X O R   9 = 5

容易验证, XOR X O R 运算满足交换律与结合律,故计算若干个数的 XOR X O R 时,不同的计算顺序不会对运算结果造成影响。从而,可以定义 K K 个非负整数A1,A2,...,AK1,AK XOR X O R 和为

A1 xor A2 xor...xor AK1 xor AK A 1   x o r   A 2   x o r . . . x o r   A K − 1   x o r   A K

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

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

输入输出格式
输入格式:

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

接下来 M M 行描述M条边,每行三个整数 Si,Ti,Di S i , T i , D i ,表示 Si S i 与 T_i之间存在一条权值为 Di D i 的无向边。

图中可能有重边或自环。

输出格式:

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

输入输出样例
输入样例#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

说明
【样例说明】

如图,路径 12435245 1 → 2 → 4 → 3 → 5 → 2 → 4 → 5 对应的XOR和为 2 XOR 1 XOR 2 XOR 4 XOR 1 XOR 1 XOR 3=6 2   X O R   1   X O R   2   X O R   4   X O R   1   X O R   1   X O R   3 = 6

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

【数据规模】

对于20%的数据, N100 N ≤ 100 M1000 M ≤ 1000 Di104 D i ≤ 10 4

对于50%的数据, N1000 N ≤ 1000 M10000 M ≤ 10000 Di1018 D i ≤ 10 18

对于70%的数据, N5000 N ≤ 5000 M50000 M ≤ 50000 Di1018 D i ≤ 10 18

对于100%的数据, N50000 N ≤ 50000 M100000M100000 M ≤ 100000 M ≤ 100000 Di1018 D i ≤ 10 18

题解

乍一看很难,然而只需要推出一个小规律就很水了:

+= 一 条 路 径 + 一 个 环 = 一 条 新 路

所以我们只需要求出所有的“基本环”,即一个单环,不包括复环或多个环串在一起,多个这样最简单的环即可表示出所有路径,我们只需要dfs求出所有基本环,用基本环的权值做线性基,在任意一条1到n的路径上贪心加入线性基就可以求出最大的异或和路径了。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=5e5;
struct sd{
    int n;ll d;
};
int n,m,cot;
ll val[M],cir[M],base[64];
bool vis[M];
vector<sd>mmp[M];
void in()
{
    scanf("%d%d",&n,&m);
    int a,b;ll c; 
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%lld",&a,&b,&c);
        mmp[a].push_back((sd){b,c});
        mmp[b].push_back((sd){a,c});
    }
}
void dfs(int f,int v)
{
    vis[v]=1;
    int t;
    for(int i=mmp[v].size()-1;i>=0;--i)
    {
        t=mmp[v][i].n;
        if(t==f)continue;
        if(vis[t]){cir[++cot]=mmp[v][i].d^val[v]^val[t];continue;}
        val[t]=mmp[v][i].d^val[v];
        dfs(v,t);
    }
}
void ji()
{
    ll u;
    for(int i=1;i<=cot;++i)
    {
        u=cir[i];
        for(int j=63;j>=0;--j)
        {
            if(u&(1ll<<j))
            if(!base[j]){base[j]=u;break;}
            else u^=base[j];
        }
    }
}
void ac()
{
    dfs(0,1);
    ji();
    ll ans=val[n];
    for(int i=63;i>=0;--i)
    if((ans^base[i])>ans)ans^=base[i];
    printf("%lld",ans);
}
int main()
{
    in();ac();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值