【Luogu P5787】二分图 /【模板】线段树分治
链接:
题目大意:
有一个 \(n\) 个节点的图,在 \(k\) 时间内有 \(m\) 条边会出现后消失,要求出每一时间段内这个图是否是二分图。
思路:
线段树分治用于离线、可撤销类的题目。
线段树维护某时间内连的边。统计答案时,用扩展域并查集查询连边是否合法。并查集不可路径压缩,因为要用栈回溯。
代码:
const int N = 3e5 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m, k;
struct node
{
int l, r, st, ed;
}e[N];
vector <int> t[N << 3];
void Modify(int p, int l, int r, int L, int R, int id)
{
if (l > R || r < L) return;
if (L <= l && r <= R)
{
t[p].push_back(id);
return;
}
int mid = l + r >> 1;
Modify (p << 1, l, mid, L, R, id);
Modify (p << 1 | 1, mid + 1, r, L, R, id);
}
struct Stack
{
int x, y, add;
};
int top = 0;
Stack stk[N << 2];
int fa[N << 1], height[N << 1];
int Find(int k) {return k == fa[k]? k: fa[k] = Find(fa[k]);}
void Merge (int u, int v)
{
int x = Find(u), y = Find(v);
if (height[x] > height[y]) x ^= y ^= x ^= y;
stk[++top] = (Stack){x, y, height[x] == height[y]};
fa[x] = y;
if(height[x] == height[y]) height[y]++;
}
void Solve (int p, int l, int r)
{
int flag = 1, lsttop = top;
for (int i = 0; i < t[p].size(); i++)
{
int u = e[t[p][i]].l, v = e[t[p][i]].r;
int x = Find(u), y = Find(v);
if (x == y)
{
for (int j = l; j <= r; j++)
printf ("No\n");
flag = 0;
break;
}
Merge (u, v + n);
Merge (v, u + n);
}
if (flag)
{
if (l == r) printf ("Yes\n");
else
{
int mid = l + r >> 1;
Solve (p << 1, l, mid);
Solve (p << 1 | 1, mid + 1, r);
}
}
for (; top > lsttop; top--)
height[fa[stk[top].x]] -= stk[top].add,
fa[stk[top].x] = stk[top].x;
}
int main()
{
n = Read(), m = Read(), k = Read();
int cnt = 0;
for (int i = 1; i <= m; i++)
{
e[i].l = Read(), e[i].r = Read(), e[i].st = Read(), e[i].ed = Read();
Modify(1, 1, k, e[i].st + 1, e[i].ed, i);
}
for (int i = 0; i <= n * 2; i++) fa[i] = i, height[i] = 1;
Solve (1, 1, k);
return 0;
}