首先考虑如何判定一张图是否是二分图,这里采用扩展域并查集。
那么对于每个时间节点,就可以暴力遍历此时的所有边,并用扩展域并查集判定。
然而这样的时间复杂度显然会爆掉,但观察到每条边出现的时间是连续段,所以考虑用区间数据结构维护,例如线段树。
把该时间段拆成 $\log n$ 段并加边,最后通过一次 dfs 求出每个叶子节点的答案。
即需要一个可撤销并查集维护这件事。注意由于需要支持撤销,所以不能路径压缩,只能按秩合并。
#include <bits/stdc++.h>
#define PII pair<int, int>
using namespace std;
const int N = 4e5 + 15;
int n, m, k;
struct Tree {
int l, r;
vector<PII> seg;
} tr[N << 2];
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void change(int u, int l, int r, PII d) {
if (l > r || tr[u].r < l || tr[u].l > r) return;
if (tr[u].l >= l && tr[u].r <= r) return tr[u].seg.push_back(d), void();
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) change(u << 1, l, r, d);
if (r > mid) change(u << 1 | 1, l, r, d);
}
int p[N], sz[N];
int find(int x) { return p[x] == x ? x : find(p[x]); }
int stk[N], top = 0;
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
p[x] = y, sz[y] += sz[x], stk[++top] = x;
}
bool ans[N];
void query(int u) {
bool flag = 1;
int pre = top;
for (PII it : tr[u].seg) {
int x = it.first, y = it.second;
if (find(x) == find(y)) {
for (int i = tr[u].l; i <= tr[u].r; i++) ans[i] = 0;
flag = 0;
break;
}
merge(it.first, it.second + n), merge(it.first + n, it.second);
}
if (flag) {
if (tr[u].l == tr[u].r) ans[tr[u].l] = 1;
else query(u << 1), query(u << 1 | 1);
}
while (top > pre) {
int x = stk[top--];
sz[p[x]] -= sz[x], p[x] = x;
}
return;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n * 2; i++) p[i] = i, sz[i] = 1;
build(1, 1, k);
for (int i = 1; i <= m; i++) {
int u, v, l, r;
scanf("%d%d%d%d", &u, &v, &l, &r);
change(1, l + 1, r, make_pair(u, v));
}
query(1);
for (int i = 1; i <= k; i++) puts(ans[i] ? "Yes" : "No");
return 0;
}