4025: 二分图
4025: 二分图
分析:
线段树分治+并查集。
以时间为下标建立一颗线段树,对于每条边u,v,l,r,在期出现的时间[l,r]的时间上打标记,表示时间[l,r]存在这条边,那么在线段树上递归到叶子结点,就是每个时刻所有出现的边。
从根节点出发,把所有的边加入,并查集维护是否合法,如果到叶子结点了,依然是合法的,那么这个时刻就是可行的。
退出一个点的时候,并查集中也要撤销的,所以不能路径压缩,可以按秩合并。
如何判断是否合法?不存在奇环。即可以黑白染色。考虑并查集如果每个点的颜色。合并时,在并查集的根上打一个标记,如果存在这个标记,说明这个集合内的点的颜色取反。那么一个点的颜色就是从这个点出发,到根节点的路径上的异或和。
同样可以CDQ分治,把每条线段拆成两个操作,在l出加入,在r处删除,询问T次。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> #define pa pair<int,int> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 200005; int col[N], h[N], ans[N], fa[N]; vector< pa > e[N << 2]; struct OPT{ int x, y, f; }; vector<OPT> g[N << 2]; void update(int l,int r,int rt,int L,int R,int u,int v) { if (L > r || R < l) return ; if (L <= l && r <= R) { e[rt].push_back(pa(u, v)); return ; } int mid = (l + r) >> 1; if (L <= mid) update(l, mid, rt << 1, L, R, u, v); if (R > mid) update(mid + 1, r, rt << 1 | 1, L, R, u, v); } int findcol(int &x) { int c = 0; while (x != fa[x]) c ^= col[x], x = fa[x]; return c; } void query(int l,int r,int rt) { bool flag = false; for (int i = 0; i < (int)e[rt].size(); ++i) { int x = e[rt][i].first, y = e[rt][i].second; int a = findcol(x), b = findcol(y); if (x == y) { if (a == b) { flag = true; for (int j = l; j <= r; ++j) ans[j] = false; break; } continue; } if (h[x] < h[y]) swap(x, y); fa[y] = x, col[y] = a ^ b ^ 1; if (h[x] == h[y]) h[x] += 1; g[rt].push_back((OPT){x, y, (h[x] == h[y])}); } int mid = (l + r) >> 1; if (!flag && l != r) query(l, mid, rt << 1), query(mid + 1, r, rt << 1 | 1); for (int i = g[rt].size() - 1; ~i; --i) { int x = g[rt][i].x, y = g[rt][i].y; fa[y] = y, col[y] = 0, h[x] -= g[rt][i].f; } } int main() { int n = read(), m = read(), T = read(); for (int i = 1; i <= n; ++i) fa[i] = i, h[i] = 1; for (int i = 1; i <= T; ++i) ans[i] = 1; for (int i = 1; i <= m; ++i) { int u = read(), v = read(), l = read() + 1, r = read(); if (l > r) continue; update(1, T, 1, l, r, u, v); } query(1, T, 1); for (int i = 1; i <= T; ++i) puts(ans[i] ? "Yes" : "No"); return 0; }