【题解】P5787 二分图 /【模板】线段树分治
概念
线段树分治是一种用于维护时间轴等的离线算法,本质上是通过维护时间轴的连续区间得到某一时刻的状态。
时间复杂度和普通线段树相同,空间复杂度为 \(O(n \log n)\).
现在常见的应用是维护有操作的图连通性问题。
例题
将每条边看成修改操作,则它的作用范围是时刻区间 \([l, r)\).
考虑对时间轴构造一棵线段树。对于线段树上的结点,假设它代表的区间是 \([l, r]\),则该结点维护所有在 \([l, r]\) 时刻均存在的边。
于是从根到叶结点的路径可以维护某一时刻存在的所有边。
假如可以快速判定二分图,我们只需要对整棵线段树进行一次遍历,动态维护当前的图就行。
判定二分图可以考虑可撤销扩展域并查集。
因为线段树树高为 \(O(\log n)\) 级别,所以每条边至多被分解成 \(O(\log n)\) 个信息,空间复杂度为 \(O(m \log n)\).
复杂度为 \(O(k \log k \log n)\)
代码
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
const int maxk = 1e5 + 5;
const int dsu_sz = maxn << 1;
const int t_sz = maxk << 2;
typedef pair<int, int> pii;
#define pb push_back
#define mp make_pair
#define swap(x, y) x ^= y ^= x ^= y
int n, m, k;
bool ans[maxk];
namespace DSU
{
int top, stk[dsu_sz], fa[dsu_sz], sz[dsu_sz];
void init() { for (int i = 1; i <= (n << 1); i++) fa[i] = i, sz[i] = 1; }
int get(int x) { return (fa[x] == x ? x : get(fa[x])); }
void merge(int x, int y)
{
x = get(x), y = get(y);
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
fa[x] = y, sz[y] += sz[x], stk[++top] = x;
}
void undo(int lst)
{
while (top > lst)
{
int u = stk[top--];
sz[fa[u]] -= sz[u], fa[u] = u;
}
}
}
namespace SGT
{
#define ls (k << 1)
#define rs (k << 1 | 1)
vector<pii> edge[t_sz];
void update(int k, int l, int r, int ql, int qr, int u, int v)
{
if ((l >= ql) && (r <= qr)) return edge[k].pb(mp(u, v)), void();
int mid = (l + r) >> 1;
if (ql <= mid) update(ls, l, mid, ql, qr, u, v);
if (qr > mid) update(rs, mid + 1, r, ql, qr, u, v);
}
void query(int k, int l, int r)
{
int lst = DSU::top;
for (pii it : edge[k])
{
int u = it.first, v = it.second;
DSU::merge(u, v + n), DSU::merge(v, u + n);
if ((DSU::get(u) == DSU::get(u + n)) || (DSU::get(v) == DSU::get(v + n))) return DSU::undo(lst), void();
}
if (l == r) ans[l] = true;
else
{
int mid = (l + r) >> 1;
query(ls, l, mid), query(rs, mid + 1, r);
}
DSU::undo(lst);
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
DSU::init();
for (int i = 1, u, v, l, r; i <= m; i++)
{
scanf("%d%d%d%d", &u, &v, &l, &r);
if (l != r) SGT::update(1, 1, k, l + 1, r, u, v);
}
SGT::query(1, 1, k);
for (int i = 1; i <= k; i++) puts(ans[i] ? "Yes" : "No");
return 0;
}