「BZOJ2115」「WC2011」 Xor

来源:互联网 发布:js href click 编辑:程序博客网 时间:2024/05/19 16:19

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 , 那么加入 bia⃗  对应位为 0 ,且不能加入其他向量使得对应位重新变为 1 , 所以不加入 bi 更优 。
如果 bi 最高位为 1 , a⃗  对应位为 0 , 那么加入 bia⃗  对应位为 1 ,且加入其他向量不影响该位 , 所以加入 bi 更优 。
如果 bi 最高位为 0 , 那么这一定是个零向量。

因此这个贪心思路是正确的。
复杂度 O(nlog22D) , D 为边权值的最大值。

My Code

#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#define B 62using 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;}