hdu 5544 Ba Gua Zhen

26 篇文章 0 订阅
2 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=5544

题意:
有一个n(<=50000)个顶点m(<=100000)条边的无向图,每条边有一个边权(0<=边权<2^60),求所有回路中边权xor和的最大值。

思路:
任意选定一个起点,从起点出发开始dfs,同时记录路径xor和,每访问到一个访问过的点表明得到了一个环,将当前异或值与起点到当前点的xor和xor一下可以得到环上的xor和,由于xor的性质,任意一个回路都可以用前面得到的环来线性表示,于是对之前得到的xor和做一次xor高斯消元,得到一组向量基,令ans=0,枚举每个向量基,用ans=max(ans,ans^base)更新答案即可,复杂度O(60*(n+m))。

这题其实重要的就是抑或高斯消元,我们最终想把每个权值都用二进制表示,每一个基代表相应的向量的最高位是那个且是1,这样最后我们就可以贪心的把每一位变成1,而不用考虑其余低位的情况(因为二进制的性质)!
这时我们需要了解一些性质:
假设集合A,其中我们将ai替换成ai xor a[j] i<>j,那么得到的新集合,这个集合中元素能xor出的值于集合A相同,而此时我们恰恰需要从高到低,把其余对应位是1的异或为0,此时无影响!

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)

using namespace std;

const int maxn = 50050;
const int maxm = 100010;

int head[maxn],edge_cnt,n,m,tot;

struct Edge{
    int u,v;
    LL val;
    int nt;
}edge[maxm<<1];

void addedge(int u,int v,LL val){
    edge[edge_cnt].u = u;
    edge[edge_cnt].v = v;
    edge[edge_cnt].val = val;
    edge[edge_cnt].nt = head[u];
    head[u] = edge_cnt++;
}

void Build_Graph(){
    memset(head,-1,sizeof(head));
    edge_cnt = 0;
    scanf("%d%d",&n,&m);
    FOR(i,0,m){
        int u,v;    LL val;
        scanf("%d%d%I64d",&u,&v,&val);
        addedge(u,v,val);
        addedge(v,u,val);
    }
}

LL val[maxm<<1];
LL d[maxn];
bool vis[maxn];

void dfs(int u,int fa){
    vis[u] = true;
    for(int i = head[u];i != -1;i = edge[i].nt){
        int v = edge[i].v;
        if(v == fa)  continue;
        if(!vis[v]){
            d[v] = d[u] ^ edge[i].val;
            dfs(v,u);
        }
        else{
            val[tot++] = d[u]^d[v]^edge[i].val;
        }
    }
}

int xorguass(int n){
    int row = 0;
    for(int i = 60;i >= 0;-- i){
        int j;
        for(j = row;j < n;++ j){
            if(val[j] & (1LL<<i))   break;
        }
        if(j != n){
            swap(val[j],val[row]);
            for(j = 0;j < n;++ j){
                if(j == row)    continue;
                if(val[j] & (1LL<<i))   val[j] ^= val[row];
            }
            ++ row;
        }
    }
    return row;
}

void work(){
    memset(vis,false,sizeof(vis));
    tot = 0;
    d[1] = 0;
    dfs(1,-1);
    n = xorguass(tot);
    LL ans  = 0;
    FOR(i,0,n)  ans ^= val[i];
    printf("%I64d\n",ans);
}

int main()
{
    int T,tCase = 0;    scanf("%d",&T);
    while(T--){
        printf("Case #%d: ",++tCase);
        Build_Graph();
        work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值