「BZOJ2115」「WC2011」 Xor

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

这里写图片描述

题解

我 BZOJ2115 A辣!
我 BZOJ2115 A辣!
我 BZOJ2115 A辣!
我会线性基辣!

线性基第一道非模板题
首先,假设你会线性基,即知道怎么求 N 个数的最大异或和。
然后我们分析这道题。
首先,题目描述非常吓人,但是仔细一分析,就会非常简单:对于一条 1n 的路径,我们可以拆成一条 11 的环与另一条 1n 的路径。于是我们可以先求出所有 11 的环的异或值,将其放入线性基预处理,在选一条 1n 的路径记其异或和向量为 a⃗ 
然后对于基中的所有向量 bi ,我们贪心的从大到小判断 a⃗  异或上 bi 会不会使答案增加,为什么这样是对的呢?
根据线性基的对角矩阵性质,如果 bi 的最高位为 1 ,那么其他 bj(ji) 对应的位置一定为 0
我们按二进制位从高到低考虑:
如果 bi 最高位为 1 , a⃗  对应位为 1 , 那么加入 bi a⃗  对应位为 0 ,且不能加入其他向量使得对应位重新变为 1 , 所以不加入 bi 更优 。
如果 bi 最高位为 1 , a⃗  对应位为 0 , 那么加入 bi a⃗  对应位为 1 ,且加入其他向量不影响该位 , 所以加入 bi 更优 。
如果 bi 最高位为 0 , 那么这一定是个零向量。

因此这个贪心思路是正确的。
复杂度 O(nlog22D) , D <script type="math/tex" id="MathJax-Element-5308">D</script> 为边权值的最大值。

My Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define B 62

using namespace std;
typedef long long ll;

struct edge{
    int to, nxt;
    ll w;
}e[200005];

int h[50005], cnt = 1;

ll a[1000005], b[105];
int n, m, N;
ll ans;

ll dis[50005];
int vis[50005];
void addedge(int x, int y, ll w){
    cnt++; e[cnt].to = y; e[cnt].nxt = h[x]; e[cnt].w = w; h[x] = cnt;
    cnt++; e[cnt].to = x; e[cnt].nxt = h[y]; e[cnt].w = w; h[y] = cnt;
}

void dfs(int x, int ii){
    vis[x] = 1;
    for(int i = h[x]; i; i = e[i].nxt){
        if((i ^ 1) == ii) continue;
        if(!vis[e[i].to]) {
            dis[e[i].to] = dis[x] ^ e[i].w;
            dfs(e[i].to, i);
        }else{
            a[++N] = dis[e[i].to] ^ dis[x] ^ e[i].w;
        }
    }
}

void solve(){
    for(int i = 1; i <= N; i ++){
        for(int j = B; j >= 0; j --){
            if((a[i] >> j) & 1){
                if(b[j]) a[i] ^= b[j];
                else{
                    b[j] = a[i];
                    for(int k = j - 1; k >= 0; k --) if(((b[j] >> k) & 1) && b[k]) b[j] ^= b[k];
                    for(int k = j + 1; k <= B; k ++) if((b[k] >> j) & 1) b[k] ^= b[j];
                    break;
                }
            }
        }
    }
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i ++){
        int x, y; ll z;
        scanf("%d%d%lld", &x, &y, &z);
        addedge(x, y, z);
    }
    dfs(1, 0);
    solve();
    ans = dis[n];
    for(int i = B; i >= 0; i --){
        if((ans ^ b[i]) > ans) ans = ans ^ b[i];
    }
    printf("%lld\n", ans);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值