CF938G Shortest Path Queries 题解

CF938G Shortest Path Queries 题解

异或有很多奇妙的小性质,在这道题里都得到了体现。

注意到异或下求出 (x,y) 的最短路等价于 dis(1,x)dis(1,y),于是先考虑静态的情形怎么做。考虑到当原图中有环时走环或许可以“无代价”地减小答案,事实上任何一个环我们都可以不花费其它代价遍历到。这也就是说我们可以在一条链上套上所有的环。至于链的选择,是随机的,因为即使最终的答案不经过这条链,也会有一个环把它消掉。其中还有很多的细节需要证明,但在异或的性质下都是正确的。那么维护环的选择可以直接开一个线性基。

现在来考虑动态的情形。维护带删除的线性基是困难的,于是考虑处理动态问题的另一种方式:线段树分治。线段树分治利于解决可撤销的问题。在线性基中删除是困难的,但是在线段树每个节点处撤销恢复原来的线性基是 O(logV) 的,完全可以接受。于是我们维护可撤销并查集,顺手维护一下此时的线性基,再维护一下每个点到根的距离即可。具体代码有一些细节实现时需要留意,结合异或的小性质就不难实现了。

代码:

#include <bits/stdc++.h>
#define N 200005
#define M 31
using namespace std;
int n, m, q;
int P[M];
void insert(int x) {
	for (int i = M - 1; ~i; --i)
		if ((x >> i) & 1) {
			if (!P[i]) {
				P[i] = x;
				break;
			}
			x ^= P[i];
		}
}
int query(int x) {
	for (int i = M - 1; ~i; --i)
		if ((x ^ P[i]) < x) x ^= P[i];
	return x;
}

int fa[N], siz[N], dor[N];
int fnd(int x) {
	return x == fa[x] ? x : fnd(fa[x]);
}
int gtds(int x) {
	return x == fa[x] ? 0 : dor[x] ^ gtds(fa[x]);
}
#define pii pair<int, int>
#define mk make_pair
pii stk[N];
int top;
void mge(int x, int y, int z) {
	int sx = fnd(x), sy = fnd(y);
	if (sx == sy) {
		insert(gtds(x) ^ gtds(y) ^ z);
		return;
	}
	if (siz[sx] > siz[sy]) swap(sx, sy), swap(x, y);
	stk[++top] = mk(sx, dor[sx]);
	dor[sx] = gtds(x) ^ gtds(y) ^ z;
	fa[sx] = sy;
	siz[sy] += siz[sx];
}
void del(int t) {
	while (top > t) {
		pii p = stk[top--];
		int x = p.first;
		siz[fa[x]] -= siz[x];
		fa[x] = x;
		dor[x] = p.second;
	}
}

struct node {
	int x, y, z;
};
struct Node {
	int l, r;
	vector<node>v;
} e[N << 2];
#define l(i) e[i].l
#define r(i) e[i].r
#define v(i) e[i].v
#define lc (p << 1)
#define rc (lc | 1)
void build(int p, int l, int r) {
	l(p) = l, r(p) = r;
	if (l == r) return;
	int mid = (l + r) >> 1;
	build(lc, l, mid);
	build(rc, mid + 1, r);
}
void update(int p, int l, int r, node x) {
	if (l > r || l > r(p) || l(p) > r) return;
	if (l <= l(p) && r(p) <= r) return v(p).push_back(x);
	update(lc, l, r, x), update(rc, l, r, x);
}

vector<int>ans;
pii que[N];
void dfs(int p) {
	int t = top;
	vector<int>vp(M + 5);
	for (int i = 0; i < M; i++) vp[i] = P[i];
	for (auto i : v(p)) mge(i.x, i.y, i.z);
	if (l(p) == r(p)) {
		int x = que[l(p)].first, y = que[l(p)].second;
		if (x == 0) return;
		ans.push_back(query(gtds(x) ^ gtds(y)));
	}
	else dfs(lc), dfs(rc);
	del(t);
	for (int i = 0; i < M; i++) P[i] = vp[i];
}

map<pii, int>mp, mv;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	iota(fa + 1, fa + n + 1, 1);
	fill(siz + 1, siz + n + 1, 1);
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		if (x > y) swap(x, y);
		mp[mk(x, y)] = 1;
		mv[mk(x, y)] = z;
	}
	cin >> q;
	build(1, 1, q);
	for (int i = 1; i <= q; i++) {
		int o, x, y;
		cin >> o >> x >> y;
		if (x > y) swap(x, y);
		if (o == 1) {
			int z;
			cin >> z;
			mp[mk(x, y)] = i;
			mv[mk(x, y)] = z;
		}
		else if (o == 2) {
			update(1, mp[mk(x, y)], i - 1, {x, y, mv[mk(x, y)]});
			mv[mk(x, y)] = -1;
		}
		else que[i] = mk(x, y); 
	}
	for (auto i : mp) {
		pii p = i.first;
		if (mv[p] != -1) update(1, i.second, q, {p.first, p.second, mv[p]});
	}
	dfs(1);
	for (auto i : ans) cout << i << '\n';
	return 0;
}
posted @   长安19路  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示