BZOJ2115 WC2011 Xor DFS+高斯消元

题意:给定一张无向图,求1到N异或和最大的路径,允许重复经过。
题解:首先跑出1到N的一条路径,答案就是在这条路径上不断加环。首先用DFS处理出所有基环的异或和(其他环一定由基环构成,重复部分异或之后就会消掉),然后就是从一堆数里选任意个数使得异或和最小了,怎么做可以去看莫涛的课件(同解01异或方程),这里我简单介绍一下。

通过高斯消元,我们对原来的数进行操作,使得所有原来的数都可以用操作之后的数来组合而成(这玩意貌似叫线性基啊)。具体做法就是从高到低暴力枚举每一位i,找到一个第i位为1的数j,然后将所有除这个数外第i位为1的数x^=j,最后消出来的b就是线性基了。直观看起来很显然,具体的证明我也不会QAQ。

然后可以暴力枚举,看某个环是不是要加进去了。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long

const int MAXN=50000+2;
const int MAXM=100000+2;
struct HASH{
    int u;
    ll w;
    HASH *next;
    HASH(){}
    HASH(int _u,ll _w,HASH *_next):u(_u),w(_w),next(_next){}
}*table[MAXN],mem[MAXM];
int N,M,C,cnt;
ll ans,d[MAXN],w[2*MAXM];
bool flag[MAXN];

void Insert(int u,int v,ll w){ table[u]=&(mem[cnt++]=HASH(v,w,table[u]));}

void DFS(int x){
    flag[x]=1;
    for(HASH *p=table[x];p;p=p->next)
        if(!flag[p->u]) d[p->u]=d[x]^p->w,DFS(p->u);
        else cnt++,w[cnt]=d[x]^d[p->u]^p->w;
}

void Gauss(){
    ll i=1;
    for(int j=60;j;j--) i<<=1;
    for(;i;i>>=1){
        int j=C+1;
        while(j<=cnt && !(w[j]&i)) j++;
        if(j>cnt) continue;
        C++;

        swap(w[C],w[j]);
        for(int k=1;k<=cnt;k++)
            if(k!=C && (w[k]&i)) w[k]^=w[C];
    }
}

int main(){
    cin >> N >> M;
    for(int i=1;i<=M;i++){
        int a,b;ll c;
        scanf("%d %d %lld",&a,&b,&c);
        Insert(a,b,c),Insert(b,a,c);
    }

    cnt=0,DFS(1);

    Gauss(),ans=d[N];
    for(int i=1;i<=C;i++) ans=max(ans,ans^w[i]);
    cout << ans << endl;

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/WDZRMPCBIT/p/6443526.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值