题目大意
给定一个\(n\)个点, \(m\)条边的无向图, 每条边在一定时间范围内存在. 要你判断每个时间点这张图是否为二分图.
\(n \le 10^5\)
\(m \le 2 \times 10^5\)
Solution
我们考虑一个合法的二分图有什么性质: 图中不存在奇环, 即环上边数(点数)为奇数的环.
考虑如何判断每个时刻是否存在奇环. 考虑我们把一条边加入一张图中可能的情况:
- 将两个不联通的块连接在一起. 不会产生新的奇环.
- 将一棵树上的两个点连接在一起. 假如这两个点之间的路径上的点数为奇数, 则会产生奇环; 否则不会产生奇环.
- 将一张图上的两个点连接在一起. 我们考虑这张图是由一棵树, 再加上一些边形成的. 我们假设在加入这条边之前的图中不存在奇环, 则假如树上这两点间的路径上的点数为奇数, 则会产生奇环; 否则不会产生奇环. 换而言之, 之前存在的偶环不影响答案.
因此我们用link-cut tree维护这张图. 我们又考虑到边会消失, 因此每次我们加入一条边时, 进行如下讨论:
- 假如这条边连接的是两颗不相连的树, 则直接加入这条边
- 假如这条边连接的是一棵树上的两个点, 则考虑是否需要更新答案. 同时我们还需要用这条边更新两点间树上所有连边中最早消失的一条(即删掉最早消失的边, 并且连接上当前边. 当然, 假如当前边的消失时间早于最早消失的边, 则不用作任何修改).
这样即可保证任意两个相连的点之间, 最晚消失的边存在于树上, 因而保证正确性.
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1; char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)1e5, M = (int)2e5, INF = (int)2e9, T = (int)1e5;
int n, m;
int ans[T + 1];
struct edge
{
int u, v, L, R;
inline int operator <(const edge &a) const {return L == a.L ? R < a.R : L < a.L;}
}edg[M];
struct linkCutTree
{
int tp;
struct node
{
int pre, suc[2], isRoot, rev;
int w, mn, sz;
inline node() {pre = -1; for(int i = 0; i < 2; ++ i) suc[i] = -1; isRoot = 1; rev = 0; sz = 1;}
}nd[N + 1 + M];
inline void initialize()
{
for(int i = 1; i <= n; ++ i) nd[i].w = INF, nd[i].mn = i;
tp = n + 1;
}
inline void pushDown(int u)
{
if(! nd[u].isRoot) pushDown(nd[u].pre);
if(nd[u].rev)
{
for(int i = 0; i < 2; ++ i) if(~ nd[u].suc[i])
swap(nd[nd[u].suc[i]].suc[0], nd[nd[u].suc[i]].suc[1]), nd[nd[u].suc[i]].rev ^= 1;
nd[u].rev = 0;
}
}
inline int getRelation(int u) {return u == nd[nd[u].pre].suc[1];}
inline void update(int u)
{
nd[u].mn = u; nd[u].sz = 1;
for(int i = 0; i < 2; ++ i) if(~ nd[u].suc[i])
{
if(nd[nd[nd[u].suc[i]].mn].w < nd[nd[u].mn].w) nd[u].mn = nd[nd[u].suc[i]].mn;
nd[u].sz += nd[nd[u].suc[i]].sz;
}
}
inline void rotate(int u)
{
int pre = nd[u].pre, prepre = nd[pre].pre, k = getRelation(u);
nd[pre].suc[k] = nd[u].suc[k ^ 1]; if(~ nd[u].suc[k ^ 1]) nd[nd[u].suc[k ^ 1]].pre = pre;
nd[u].pre = prepre; if(! nd[pre].isRoot) nd[prepre].suc[getRelation(pre)] = u;
nd[pre].pre = u; nd[u].suc[k ^ 1] = pre;
if(nd[pre].isRoot) nd[pre].isRoot = 0, nd[u].isRoot = 1;
update(pre); update(u);
}
inline void splay(int u)
{
pushDown(u);
while(! nd[u].isRoot)
{
if(! nd[nd[u].pre].isRoot) rotate(getRelation(u) == getRelation(nd[u].pre) ? nd[u].pre : u);
rotate(u);
}
}
inline void access(int u)
{
splay(u);
if(~ nd[u].suc[1])
{
nd[nd[u].suc[1]].isRoot = 1;
nd[u].suc[1] = -1; update(u);
}
while(~ nd[u].pre)
{
int pre = nd[u].pre;
splay(pre);
if(~ nd[pre].suc[1])
{
nd[nd[pre].suc[1]].isRoot = 1;
nd[pre].suc[1] = -1; update(pre);
}
nd[pre].suc[1] = u; nd[u].isRoot = 0; update(pre);
splay(u);
}
}
inline void makeRoot(int u)
{
access(u); swap(nd[u].suc[0], nd[u].suc[1]); nd[u].rev ^= 1;
}
inline int get(int u)
{
access(u);
while(~ nd[u].suc[0]) u = nd[u].suc[0];
return u;
}
inline int newNode(int w) {nd[tp].w = w; nd[tp].mn = tp; return tp ++;}
inline void link(int id)
{
int u = edg[id].u, v = edg[id].v;
if(get(u) == get(v))
{
makeRoot(u); access(v);
int x = nd[v].mn;
if(nd[x].w > edg[id].L && nd[v].sz + 1 >> 1 & 1) for(int i = edg[id].L + 1; i <= min(nd[x].w, edg[id].R); ++ i) ans[i] = 1;
if(nd[x].w > edg[id].R) return;
access(x); nd[nd[x].suc[0]].pre = -1; nd[nd[x].suc[0]].isRoot = 1; nd[x].suc[0] = -1;
makeRoot(v); access(x); nd[nd[x].suc[0]].pre = -1; nd[nd[x].suc[0]].isRoot = 1; nd[x].suc[0] = -1;
}
makeRoot(u); makeRoot(v);
int x = newNode(edg[id].R); nd[x].pre = u; nd[v].pre = x;
return;
}
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
#endif
using namespace Zeonfai;
n = getInt(), m = getInt(); int T = getInt();
LCT.initialize();
for(int i = 0; i < m; ++ i) edg[i].u = getInt(), edg[i].v = getInt(), edg[i].L = getInt(), edg[i].R = getInt();
sort(edg, edg + m);
for(int i = 0; i < m; ++ i) LCT.link(i);
for(int i = 1; i <= T; ++ i) puts(! ans[i] ? "Yes" : "No");
}