zoukankan      html  css  js  c++  java
  • BZOJ 4025 二分图

    题目大意

    给定一个(n)个点, (m)条边的无向图, 每条边在一定时间范围内存在. 要你判断每个时间点这张图是否为二分图.
    (n le 10^5)
    (m le 2 imes 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");
    }
    
    
  • 相关阅读:
    简单工厂模式&工厂方法模式&抽象工厂模式的区别及优缺点及使用场景
    JDK1.8的新特性
    在Button样式中添加EventSetter,理解路由事件
    关于C#低版本升级高版本时,项目中引用Microsoft.Office.Interop.Word,程序提示不存在类型或命名空间名office.
    无法安装或运行此应用程序。该应用程序要求首先在"全局程序集缓存(GAC)"中安装程序集
    C#winform跨窗体传值和调用事件的办法
    C#线程处理:七、线程实列
    C#线程处理:六、线程同步(三)
    C#线程处理:五、线程同步(二)
    C#线程处理:四、线程同步
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7502056.html
Copyright © 2011-2022 走看看