【BZOJ2115】【WC2011】Xor

【题目链接】

【思路要点】

  • 任取一条1到\(N\)的路径,考虑一个与其相交的环,显然,我们可以半路“绕”一次这个环,使路径的权值Xor上环的权值。这启发我们问题很可能与线性基有关。
  • 再考虑一个不与其相交的环,由于图的联通性,我们可以走出路径“绕”一次这个环,再原路返回,多走的路被走了两次,Xor和为0。因此,路径的权值同样Xor上了环的权值。
  • 所以,我们如果能知道图中所有环的权值,并制成线性基,接下来只需要任取一条1到\(N\)的路径,将其权值放入线性基中贪心即可。
  • 图中环的个数可能是指数级的,但它们都可以通过一些基本环Xor得到。
  • 任取一棵生成树,所有非树边与树上的一段路径对应了一个环,可以证明,由这些环相互Xor,可以得到图中所有的环。
  • 所以,对这些环权值求线性基,将路径权值放入线性基中贪心即可。
  • 时间复杂度\(O((N+M)LogV)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	100005
#define MAXLOG	62
struct edge {int dest; long long len; };
vector <edge> a[MAXN];
int n, m, f[MAXN];
int x[MAXN], y[MAXN];
long long w[MAXN], length[MAXN];
int depth[MAXN], father[MAXN][MAXLOG];
long long value[MAXN][MAXLOG], ans;
bool mark[MAXN];
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int F(int x) {
	if (f[x] == x) return x;
	else return f[x] = F(f[x]);
}
void dfs(int pos, int fa, long long val) {
	depth[pos] = depth[fa] + 1;
	father[pos][0] = fa;
	value[pos][0] = val;
	for (int i = 1; i < MAXLOG; i++) {
		father[pos][i] = father[father[pos][i - 1]][i - 1];
		value[pos][i] = value[pos][i - 1] ^ value[father[pos][i - 1]][i - 1];
	}
	if (pos == n) ans = value[pos][MAXLOG - 1];
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i].dest != fa) dfs(a[pos][i].dest, pos, a[pos][i].len);
}
long long get(int x, int y) {
	long long ans = 0;
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] <= depth[y]) {
			ans ^= value[x][i];
			x = father[x][i];
		}
	if (x == y) return ans;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			ans ^= value[x][i];
			x = father[x][i];
			ans ^= value[y][i];
			y = father[y][i];
		}
	return ans ^ value[x][0] ^ value[y][0];
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]), read(w[i]);
		if (F(x[i]) == F(y[i])) continue;
		f[F(x[i])] = F(y[i]);
		a[x[i]].push_back((edge) {y[i], w[i]});
		a[y[i]].push_back((edge) {x[i], w[i]});
	}
	dfs(1, 0, 0);
	for (int i = 1; i <= m; i++)
		length[i] = get(x[i], y[i]) ^ w[i];
	for (int i = MAXLOG; i >= 0; i--) {
		long long tmp = 1ll << i;
		int pos = 0;
		for (int j = 1; j <= m; j++)
			if (length[j] & tmp) {
				pos = j;
				break;
			}
		if (pos == 0) continue;
		long long value = length[pos];
		if ((ans & tmp) == 0) ans ^= value;
		for (int j = 1; j <= m; j++)
			if (length[j] & tmp) length[j] ^= value;
	}
	printf("%lld\n", ans);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值