【线性基/神仙题】P4151 [WC2011]最大XOR和路径

Description

给定一个无向连通图,边有边权,求一个 \(1~\sim n\) 的路径,最大化边权的异或和。如果一条边经过多次则计算多次。

Input

第一行是两个整数 \(n,m\) 代表点数和边数

下面 \(m\) 行每行三个整数描述一条边

Output

输出一行一个整数代表答案

Hint

\(1~\leq~n~\leq~50000,1~\leq~m~\leq~100000,1~\leq~\) 边权 \(\leq~10^{18}\)

Solution

首先注意到一个结论:对于所有的简单环,环上边权的异或和都可以无代价的获取。原因是可以从一号点出发进入该环绕一圈后原路返回。由于一条路径绕两边对答案的贡献为 \(0\) ,所以这些简单环的异或和都可以无代价取得。那么现在问题就转化成了寻找一条 \(1\)\(n\) 的路径,再异或上一些简单环的异或和,最大化答案。

我们考虑应该寻找哪一条路径:事实上任选一条路径即可。原因是选择的路径和答案路径一定可以构成一个环,所以异或上该环的权值就可以得到最优解。

考虑最大化异或和可以使用线性基解决。时间复杂度 \(O(m~+~n~\log^2d)\),其中 \(d\) 为边权

Code

#include <cstdio>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#define printtime()
#else
#include <ctime>
#define printtime() printf("Times used  = %ld ms\n", clock())
#endif
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
	const int L = 1000000;
	char buf[L], *front=buf, *end=buf;
	char GetChar() {
		if (front == end) {
			end = buf + fread(front = buf, 1, L, stdin);
			if (front == end) return -1;
		}
		return *(front++);
	}
}

template <typename T>
inline void qr(T &x) {
	char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
	if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
	char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
	if (ch == '.') {
		ch = IPT::GetChar();
		double base = 1;
		while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
	}
	if (lst == '-') x = -x;
}

namespace OPT {
	char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
	if (x < 0) {x = -x, putchar('-');}
	int top=0;
	do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
	while (top) putchar(OPT::buf[top--]);
	if (pt) putchar(aft);
}

const int maxn = 50010;
const int maxm = 200010;
const int maxl = 61;

struct Edge {
	int to;
	ll v;
	Edge *nxt;
};
Edge edge[maxm], *hd[maxn]; int ecnt;
inline void cont(ci from, ci to, cl v) {
	Edge &e = edge[++ecnt];
	e.to = to; e.nxt = hd[from]; e.v = v; hd[from] = &e;
}

int n, m;
ll ans;
bool vis[maxn];
ll val[maxn], lb[maxl];

void reading();
void insert(ll);
void dfs(ci, cl);


int main() {
	freopen("1.in", "r", stdin);
	qr(n); qr(m);
	reading();
	dfs(1, 0);
	for (int i = maxl - 1; ~i; --i) {
		ans = std::max(ans, ans ^ lb[i]);
	}
	qw(ans, '\n', true);
	printtime();
	return 0;
}

void reading() {
	int a, b; ll c;
	for (int i = 1; i <= m; ++i) {
		a = 0; b = 0; c = 0; qr(a); qr(b); qr(c);
		cont(a, b, c); cont(b, a, c);
	}
}

void dfs(ci u, cl tp) {
	vis[u] = true; val[u] = tp;
	if (u == n) ans = tp;
	for (Edge *e = hd[u]; e; e = e->nxt) {
		int to = e->to;
		if (!vis[to]) dfs(to, tp ^ e->v);
		else insert(tp ^ e->v ^ val[to]);
	}
}

void insert(ll x) {
	for (int i = maxl - 1; ~i; --i) if (x & (1ll << i)) {
		if (lb[i]) {
			x ^= lb[i];
		} else {
			lb[i] = x;
			for (int j = maxl - 1; j > i; --j) if (lb[j]) {
				lb[j] ^= x;
			}
			for (int j = 0; j < i; ++j) if (lb[j] & (1ll << i)) {
				lb[i] ^= lb[j];
			}
			break;
		}
	}
}
posted @ 2019-01-22 19:41  一扶苏一  阅读(350)  评论(0编辑  收藏  举报