BZOJ 2115 (线性基)详解

题目描述

输入

第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。

输出

仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。

样例输入

5 7
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2

样例输出

6

提示

写这题必须了解异或的这个性质c^b^c = b;

因为题目是存在环的,所以我们将每个环的异或和用a[i]表示,用的dfs抛出每个环上的异或值,由于c^b^c = b的性质,我们可以知道,从起点出发经过一个环后回到起点时,所得的异或和为该环的异或和。所以我们可以求出a的线性基,在得到任意一条1到n路径上的异或和便可得到异或和的最大值。

下面上代码

#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stdio.h>
#include <map>
#include<ctype.h>
using namespace std;

typedef long long LL;
const int MAX_BASE = 60;
const int MAX = 200000 + 50;
LL a[MAX]; // 用来存每个环的异或和 ,注意a数组开大一点,因为不确定有几个环
LL b[MAX_BASE + 5]; // 储存线性基

int head[MAX];
int vis[MAX];
int cnt = 0; // 记录环的个数
int k = 0; // 记录边的条数
LL d[MAX];
struct Edge
{
	int to;
	int next;
	LL w;
}edge[MAX];

void add(int a, int b, LL c){ //前向星建图
	edge[k].to = b;
	edge[k].next = head[a];
	edge[k].w = c;
	head[a] = k++;
}
void dfs(int rt){
	vis[rt] = 1;
	for(int e = head[rt]; e != -1; e = edge[e].next){
		int v = edge[e].to;
		if(v == rt){
			continue;
		}
		LL w = edge[e].w;
		if(vis[v]){ // 走到环的起点
			a[cnt++] = d[v] ^ d[rt] ^ w; //根据c ^ b ^c = b 的性质,此处需对d[v]异或,便可得到该环的异或和
		} else{
			d[v] = d[rt] ^ w; //记录的到1到v的异或和(不唯一,但只记录一种情况)
			dfs(v);
		}
	}
}

void prepare() { //建立所有环异或和的线性基
    memset(b, 0, sizeof b);
    for (int i = 0; i < cnt; ++i){
        for (int j = MAX_BASE; j >= 0; --j){
            if (a[i] >> j & 1) {
                if (b[j]) a[i] ^= b[j];
                else {
                    b[j] = a[i], cnt++;
                    for (int k = j - 1; k >= 0; --k) if (b[k] && ((b[j] >> k) & 1)) b[j] ^= b[k];
                    for (int k = j + 1; k <= MAX_BASE; ++k) if ((b[k] >> j) & 1) b[k] ^= b[j];
                    break;
                }
            }
        }
    }
}


int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	memset(head, -1, sizeof(head));
	for(int i = 0; i < m; i++){
		int a, b;
		LL c;
		scanf("%d%d%lld", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	}

	dfs(1);
	prepare();

	LL ans = d[n];
	for(int i = 60; i >= 0; i--){ // 得到结果
		if((ans ^ b[i]) > ans){
			ans ^= b[i];
		}
	}

	printf("%lld\n", ans);
    return 0;
}
扫描二维码关注公众号,回复: 5795180 查看本文章

猜你喜欢

转载自blog.csdn.net/weixin_43737952/article/details/88564891