BZOJ 2115 Xor(线性基)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2115

题目大意:中文题就不解释题意了-,-

题目思路:由异或的性质我们可以知道,当我们从结点1出发走到一个环,再从这个环回到结点1,所得到的值正好是环上所有边的权值异或和。因为你走到一个环再回来,环上的边只会被走过一次,这些值就能得到,而走到环的边则会走两次,这些值就会被抵消掉。这样问题就可以转化成了,从结点1开始直接走到结点n的边权的异或和d[n],同时再走几个环所能得到最大异或值。我们可以通过dfs先找出图中的环,将环上边的异或值存在一个数组a中,这样就变成了从数组a中任取几个数使得这几个数与d[n]异或后得到的值最大,这就能用线性基来解决了。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int inf = 0x3f3f3f3f;
const int MX = 5e4 + 5;

struct LB {
	ll d[61], p[61];
	int cnt, mx;
	LB() {
		memset(d, 0, sizeof(d));
		memset(p, 0, sizeof(p));
		cnt = 0, mx = 61;
	}
	void init() {
		memset(d, 0, sizeof(d));
		memset(p, 0, sizeof(p));
	}
	bool add(ll val) {
		/*插入时判断之前是否有数会与val异或得0,判第k小时如果有为0的情况,k要减一*/
		for (int i = mx - 1; i >= 0; i--) {
			if (val & (1LL << i)) {
				if (!d[i]) {
					d[i] = val; break;
				}
				val ^= d[i];
			}
		}
		return val > 0;
	}
	bool query(ll val) { // 查询val这个数是否存在
		for (int i = mx - 1; i >= 0; i--) {
			if (val & (1LL << i)) {
				if (!d[i]) return 0;
				val ^= d[i];
			}
		}
		return 1;
	}
	ll query_max(ll val) {
		ll ret = val;
		for (int i = mx - 1; i >= 0; i--) if ((ret ^ d[i]) > ret) ret ^= d[i];
		return ret;
	}
	ll query_min() {
		for (int i = 0; i < mx; i++) if (d[i]) return d[i];
		return 0;
	}
	void rebuild() {//消元,保存到p数组
		cnt = 0;
		for (int i = 0; i < mx; i++) {
			for (int j = 0; j < i; j ++ )
				if (d[i] & (1LL << j)) d[i] ^= d[j];
		}
		for (int i = 0; i < mx; i++) if (d[i]) p[cnt++] = d[i];
	}
	ll query_kth(ll k) { //使用前需要rebuild
		ll ret = 0;
		if (k >= (1LL << cnt)) return -1;
		for (int i = cnt - 1; i >= 0; i--) if (k & (1LL << i)) ret ^= p[i];
		return ret;
	}
	ll find(ll x) { //找x是第几大的数,需保证x一定在
		ll ret = 0, c = 0;
		for (int i = 0; i < mx; i++) {
			if (d[i]) {
				if (x >> i & 1) ret += (1LL << c);
				c++;
			}
		}
		return ret;
	}
	LB operator+(const LB & _A)const {  //合并
		LB ret = *this;
		for (int i = mx - 1; i >= 0; i--) if (_A.d[i]) ret.add(_A.d[i]);
		return ret;
	}
} base;

int n, m, cnt;
bool vis[MX];
ll d[MX], a[MX * 20];
vector<pll>E[MX];

void dfs(int u, int fa) {
	vis[u] = 1;
	for (int i = 0; i < (int)E[u].size(); i++) {
		pll nw = E[u][i];
		int v = nw.fi; ll w = nw.se;
		if (v == fa) continue;
		if (!vis[v]) {
			d[v] = d[u] ^ w;
			dfs(v, u);
		} else a[cnt++] = d[u] ^ d[v] ^ w;
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		int u, v; ll w;
		scanf("%d%d%lld", &u, &v, &w);
		E[u].pb(MP(v, w));
		E[v].pb(MP(u, w));
	}
	dfs(1, 0);
	for (int i = 0; i < cnt; i++) base.add(a[i]);
	ll ans = base.query_max(d[n]);
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/82596059