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 了,果然老年选手就是拉啊。