CF1555F Good Graph

CF1555F Good Graph

题意

给你一个有 \(n\) 个点的空图,然后每次你可以向图里选择加一条边,这条边可以加进去,当且仅当加进去之后图中的每个简单环的异或和都是 \(1\)。这里简单环的定义是没有相同顶点的环。

每次可以向图里添加一条边,你需要判断是否合法,然后合法就向里加。(加的边权值都是 \(0\) 或者 \(1\)

题解

我们可以发现,如果一条边最多只会出现在一个简单环里,我们只要用 \(\text{LCT}\) 维护这个连通块的结构,然后支持查询路径异或和,给整条路径打标记即可。

#include <bits/stdc++.h>
using std::cin;
using std::cout;
const int N = 8e5 + 10;
int n, q;
struct Node {
	int val, fa, son[2],  circletag, circle, xorsum, qs, revtag;
} nd[N];
#define val(k) nd[k].val
#define fa(k) nd[k].fa
#define son(i, k) nd[i].son[k]
#define circletag(i) nd[i].circletag
#define circle(k) nd[k].circle
#define xorsum(k) nd[k].xorsum
#define qs(k) nd[k].qs
#define revtag(k) nd[k].revtag
inline void maintain(int u) {
	xorsum(u) = xorsum(son(u, 0)) ^ xorsum(son(u, 1)) ^ val(u);
	qs(u) = circle(u) | qs(son(u, 0)) | qs(son(u, 1));
}
inline void cir(int u) {
	circletag(u) = 1;
	circle(u) = 1;
	qs(u) = 1;
}
inline void rev(int u) {
	revtag(u) ^= 1;
}
inline void down(int u) {
	if (circletag(u)) {
		circletag(u) = 0;
		circle(u) = u > n;
		if (son(u, 0)) {
			cir(son(u, 0));
		}
		if (son(u, 1)) {
			cir(son(u, 1));
		}
	}
	if (revtag(u)) {
		revtag(u) = 0;
		std::swap(son(u, 0), son(u, 1));
		if (son(u, 0)) {
			rev(son(u, 0));
		}
		if (son(u, 1)) {
			rev(son(u, 1));
		}
	}
}
inline bool no_root(int u) {
	return son(fa(u), 0) == u || son(fa(u), 1) == u;
}
void rotate(int u) {
	int p = fa(u), isl = u == son(p, 1);
	if (no_root(p)) {
		son(fa(p), p == son(fa(p), 1)) = u;
	}
	fa(u) = fa(p);
	fa(p) = u;
	if (son(u, isl ^ 1)) {
		fa(son(u, isl ^ 1)) = p;
	}
	son(p, isl) = son(u, isl ^ 1);
	son(u, isl ^ 1) = p;
	maintain(p);
}
void splay(int u) {
	static int top, stk[N];
	int x = u;
	stk[top = 1] = x;
	for ( ; no_root(x); x = fa(x)) { stk[++top] = fa(x); }
	for ( ; top; ) { down(stk[top--]); }
	while (no_root(u)) {
		if (no_root(fa(u))) {
			rotate((son(fa(u), 1) == u) == (son(fa(fa(u)), 1) == fa(u)) ? fa(u) : u);
		}
		rotate(u);
	}
	maintain(u);
}
void access(int u) {
	for (int v = 0; u; u = fa(v = u)) {
		splay(u);
		son(u, 1) = v;
		maintain(u);
	}
}
inline void make_root(int u) {
	access(u);
	splay(u);
	rev(u);
}
int find_root(int u) {
	access(u);
	splay(u);
	down(u);
	while (son(u, 0)) {
		down(u = son(u, 0));
	}
	splay(u);
	return u;
}
void link(int u, int v) {
	make_root(u);
	if (find_root(v) != u) {
		fa(u) = v;
	}
}
void cut(int u, int v) {
	make_root(u);
	access(v);
	splay(v);
	if (find_root(v) != u || fa(v) != u || son(v, 0)) {
		return;
	}
	son(u, 1) = fa(v) = 0;
	maintain(u);
}
void split(int u, int v) {
	make_root(u);
	access(v);
	splay(v);
}
int main() {
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	for (int u, v, w, T = 1; T <= q; ++T) {
		cin >> u >> v >> w;
		make_root(u);
		if (find_root(v) != u) {
			val(T + n) = xorsum(T + n) = w;
			link(T + n, u);
			link(T + n, v);
			cout << "YES\n";
		}
		else {
			split(u, v);
			if (xorsum(v) ^ w != 1 || qs(v)) {
				cout << "NO\n";
			}
			else {
				cout << "YES\n";
				cir(v);
			}
		}
	}
	return 0;
}

现在已经不会写 LCT 了,果然老年选手就是拉啊。

posted @ 2021-08-01 23:08  siriehn_nx  阅读(56)  评论(0编辑  收藏  举报