题目描述
神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。
输入输出格式
输入格式
输入数据的第一行是三个整数n,m,T。
第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。
输出格式
输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。
输入输出样例
输入样例#1
3 3 3
1 2 0 2
2 3 0 3
1 3 1 2
输出样例#1
Yes
No
Yes
解题分析
嗯,一道LCT的好题。
首先, 我们可以知道二分图有一个性质:不会存在奇环(因为在二分图中边一定只能从一边到另一边)。那么现在我们的任务就成了确定这个图内是否有奇环。
我们可以将每条边看做两个事件, 按加入和删除的时间排序。
对于每个加入操作,我们考虑:
①原来两个点不连通, 直接加入即可。
②原来两个点已连通, 出现了环, 怎么办? 其实只需要将环中删除时间最早的一条边找到, 将两条边中删除时间较早的删掉并用一个数组记录即可, 这样保证了不会有出现在环中的边被删掉, 而需要在数组中寻找另一条边补在树上的情况。 因为只要奇环不被破坏,即我们确定的环中最早被删除的边不被删除,无论怎样连环奇环都不会改变, 所以这样不会影响答案。
其实就是维护了一棵关于删除时间的最大生成树。
对于每个删边操作:
①如果在数组中被记录过, 更新奇环数量(减一)
②如果在生成树上, 直接砍掉即可。
其实还是很简单的...
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <set>
#include <cmath>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
#define MX 100050
#define inf 100000000
template <class T>
IN void in (T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
namespace LCT
{
bool inq[MX << 1], on[MX << 1];
struct Node
{
int son[2], fat, mn, siz, val, pos;
bool rev;
}tree[MX << 2];
struct Edge
{
int a, b, out;
}edge[MX << 2];
int st[MX], top, dot, line, limit, tot, ct;
IN void pushrev(const int &now) {std::swap(ls, rs), tree[now].rev ^= 1;}
IN void pushdown (const int &now)
{
if(tree[now].rev)
{
if(ls) pushrev(ls);
if(rs) pushrev(rs);
tree[now].rev = false;
}
}
IN bool get(const int &now) {return tree[dad].son[1] == now;}
IN bool nroot(const int &now) {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
IN void pushup(const int &now)
{
tree[now].mn = tree[now].val; tree[now].pos = now; tree[now].siz = now > 100000;
if(ls)
{
tree[now].siz += tree[ls].siz;
if(tree[ls].mn < tree[now].mn) tree[now].mn = tree[ls].mn, tree[now].pos = tree[ls].pos;
}
if(rs)
{
tree[now].siz += tree[rs].siz;
if(tree[rs].mn < tree[now].mn) tree[now].mn = tree[rs].mn, tree[now].pos = tree[rs].pos;
}
}
IN void rotate(const int &now)
{
R bool dir = get(now);
R int fa = dad, grand = tree[fa].fat;
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
if(nroot(fa)) tree[grand].son[get(fa)] = now;
tree[now].fat = grand;
tree[now].son[dir ^ 1] = fa;
tree[fa].fat = now;
pushup(fa);
}
IN void splay(int now)
{
top = 0; R int x = now, fa, grand;
st[++top] = x;
W (nroot(x)) x = tree[x].fat, st[++top] = x;
W (top) pushdown(st[top--]);
W (nroot(now))
{
fa = dad, grand = tree[fa].fat;
if(nroot(fa)) rotate(get(fa) == get(now) ? fa : now);
rotate(now);
}
pushup(now);
}
IN void access(R int now)
{
for (R int x = 0; now; x = now, now = dad)
splay(now), rs = x, pushup(now);
}
IN void make_root(const int &now)
{access(now), splay(now), pushrev(now);}
IN int find_root(R int now)
{
access(now), splay(now);
W (ls) pushdown(now), now = ls;
return now;
}
IN void split(const int &x, const int &y)
{make_root(x), access(y), splay(y);}
IN void cut (const int &x, const int &y)
{
split(x, y);
tree[x].fat = tree[y].son[0] = 0;
pushup(y);
}
IN void link(const int &x, const int &y)
{make_root(x), tree[x].fat = y;}
IN void link(const int &x, const int &y, const int &tim, const int &id)
{
if(x == y) //注意有自环的情况!
{
inq[id] = true; ct++;
return;
}
make_root(x);
if(find_root(y) != x)
{
link(y, id + 100001), link(x, id + 100001);
on[id] = true;
return;
}
else
{
split(x, y);
if(tree[y].mn < tim)
{
int tar = tree[y].pos - 100001;
if(tree[y].siz & 1 ^ 1) inq[tar] = true, ct++;
cut(edge[tar].a, tar + 100001), cut(edge[tar].b, tar + 100001);
link(x, id + 100001), link(y, id + 100001);
on[tar] = false; on[id] = true;
}
else
{
if(tree[y].siz & 1 ^ 1)
inq[id] = true, ct++;
}
}
}
IN void cut(const int &x, const int &y, const int &id)
{
if(on[id])
{
cut(edge[id].a, id + 100001);
cut(edge[id].b, id + 100001);
on[id] = false;
}
else if(inq[id]) return inq[id] = false, ct--, void();
}
}
using namespace LCT;
struct Event
{
bool typ;
int a, b, tim, id;
}eve[MX << 2];
IN bool operator < (const Event &x, const Event &y)
{return x.tim < y.tim;}
int main()
{
//freopen("data.in", "r", stdin);
int a, b, c, d, cur = 1, tim = 0;
in(dot), in(line), in(limit);
for (R int i = 1; i <= line; ++i)
{
in(a), in(b), in(c), in(d);
eve[++tot] = {0, a, b, c, i};
eve[++tot] = {1, a, b, d, i};
tree[i + 100001].val = tree[i + 100001].mn = d;
tree[i + 100001].pos = i + 100001;
tree[i + 100001].siz = 1;
edge[i] = (Edge){a, b, d};
}
std::sort(eve + 1, eve + 1 + tot);
for (R int i = 1; i <= dot; ++i)
{
tree[i].pos = i;
tree[i].val = tree[i].mn = inf;
}
bool flag;
W(tim < limit)
{
++tim;
W (eve[cur].tim < tim && cur <= tot)
{
if(!eve[cur].typ) link(eve[cur].a, eve[cur].b, edge[eve[cur].id].out, eve[cur].id);
else cut(eve[cur].a, eve[cur].b, eve[cur].id);
cur++;
}
if(ct == 0) puts("Yes");
else puts("No");
}
}
PS:此题的重载小于运算符把博主坑爽了……
原来博主是这么写的:
IN bool operator < (const Event &x, const Event &y)
{return x.tim == y.tim ? (!x.typ) : x.tim < y.tim;}
感觉其实这样还要科学一点, 因为可能某条边加入和删除在同一时间…
但是在BZOJ上WA了…
因为这样比较的话可能出现x < y 而 y < x的情况, 所以sort的时候RE了……(为什么会是WA…Debug了1h才发现…)