[bzoj2115][线性基]Xor

2115: [Wc2011] Xor

Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 4344 Solved: 1812
[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

这里写图片描述

Source

sol:

求从1到n的路径的最大xor值。考虑这道题是一张无向图,不是很好做。假设这题是一颗树,求任意两点间的路径的最大xor值,我们指定1为根,记f[i]为1到i的xor值,那么i到j的路径xor就是f[i]^fj。考虑把这个结论推广到图上。我们先把原图选出一些边做一个生成树出来。那么i到j的xor值在树上是唯一的,但是这个树外还有一些边,和树边一起构成了一些环,如果我们经过了一些非树边,然后到达树上的一个节点,这时候的xor值怎么表示呢?画图容易发现,i到j的xor值就是i到j在树上的xor值 xor上经过的非树边和树边形成的环(这个描述不清楚,如果有动手画图应该明白我什么意思)。那么这题就很清晰了,我们先建一个生成树,求1到n的xor值(记作ans),然后还有一些环,我们可以经过这些环(因为我从任何一个点,都可以走到一个环上,然后再走回这个点,所以环的值总能被取到),最大化这个ans xor 环的价值。上线性基贪心即可,如果ans的某一位为0,xor上对应的基。

我好像不会求环的值?假的,每条非树边都对应了一个环,随便搞即可。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double s64;
int n,m;
inline int read()
{
    char c;
    int res,flag=0;
    while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
    res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
    return flag?-res:res;
}
const int N=110000;
struct cc
{
    int x,y;
    ll z;
}e[N];
int top,father[N];
ll f[N],val[N];
int fir[N],nex[N],go[N],tot;
inline void add(int x,int y,ll z)
{
    nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;val[tot]=z;
    nex[++tot]=fir[y];fir[y]=tot;go[tot]=x;val[tot]=z;
}
inline int find(int x)
{
    return father[x]==x?x:father[x]=find(father[x]);
}
inline bool unit(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx!=fy)
    {
        father[fy]=fx;
        return 1;
    }
    return 0;
}
inline void dfs(int u,int fa)
{
    int e,v;
    for(e=fir[u];v=go[e],e;e=nex[e])
    if(v!=fa)
    {
        f[v]=f[u]^val[e];
        dfs(v,u);
    }
}
ll c[N];
int b[N];
int main()
{
//  freopen("2115.in","r",stdin);
//  freopen(".out","w",stdout);
    n=read();
    m=read();
    for(int i=1;i<=n;++i) father[i]=i;
    for(int i=1;i<=m;++i)
    {
        int x,y;ll z;
        x=read();
        y=read();
        scanf("%lld",&z);
        if(unit(x,y)) add(x,y,z);
        else e[++top]=(cc){x,y,z};
    }
    dfs(1,1);
    for(int i=1;i<=top;++i)
    c[i]=f[e[i].x]^f[e[i].y]^e[i].z;
    ll ans=f[n];
    for(int i=1;i<=top;++i)
    {
        int fir=-1;
        for(int j=60;j>=0;--j)
        if((c[i]>>j)&1)
        {
            if(b[j]) c[i]^=c[b[j]];
            else fir=max(fir,j);
        }
        if(fir!=-1)
        {
            for(int j=60;j>=0;--j)
            if(b[j]&&((c[b[j]]>>fir)&1))
            c[b[j]]^=c[i];
            b[fir]=i;
        }
    }
    for(int j=60;j>=0;--j)
    if(!(ans>>j&1))
    ans^=c[b[j]];
    cout<<ans;
}

1A美滋滋

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值