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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章