[BZOJ 4025] 二分图

26 篇文章 0 订阅

BZOJ传送门

题目描述

神犇有一个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才发现…)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值