• BZOJ4025 二分图 [线段树分治]


    二分图

    题目描述见链接 .


    color{red}{正解部分}

    先考虑如何判断是否 二分图, 可以使用 带权并查集 实现, 具体来说:

    加入边 x,yx, y 时, 设其祖先分别为 t1t_1, t2t_2,

    • t1=t2t_1 = t_2, 若 d[x]d[y]1d[x] igoplus d[y] igoplus 1奇数, 则直接判断 不为二分图, 否则不将此边加入 .
    • t1t2t_1 ot = t_2, 在并查集中, 我们将 t1t_1 接到 t2t_2 上, t1,t2t_1,t_2 之间的路径长度 奇偶性d[t1]=d[x]d[y]1d[t_1] = d[x] igoplus d[y]igoplus 1 .

    再考虑如何维护边的存在, 使用 线段树分治,
    每条边都会有一个出现的时间区间 [L,R][L, R], 考虑建立下标为 [1,T][1, T]线段树 ,
    将每条边的时间区间 [L,R][L, R] 分成 logTlog T 个区间放在 线段树 的节点中,

    然后在 线段树DFSDFS, 在到一个节点时执行当前节点存储的所有加边操作,

    • 回溯时 栈序 撤回之前操作 .
    • 在向下 dfsdfs 过程中, 若已经出现 奇环, 则直接 returnreturn, 其子树内的答案全部为 No.
    • 若顺利到达 叶子节点, 没有出现 奇环, 则该时刻答案为 Yes .

    因为要对 并查集 进行撤回操作, 所以要合并时要 按秩合并 .


    color{red}{实现部分}

    • 注意 回溯栈 的数组大小与 线段树 节点 数组大小同阶 .
    #include<bits/stdc++.h>
    #define reg register
    #define pb push_back
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 200005;
    
    int N;
    int M;
    int Tim;
    int F[maxn];
    int ds[maxn];
    int Rk[maxn];
    int Ans[maxn];
    
    struct EDGE{ int u, v; } E[maxn];
    
    struct Stak{ int top; EDGE b[maxn<<2]; } stk;
    
    int Find(int x){ return F[x]==x?x:Find(F[x]); }
    
    int ged(int x){ return F[x]==x?0:ged(F[x])^ds[x]; }
    
    struct Segment_Tree{
    
            struct Node{ int l, r; std::vector<int> b; } T[maxn << 2];
    
            void Build(int k, int l, int r){
                    T[k].l = l, T[k].r = r;
                    if(l == r) return ; int mid = l+r >> 1;
                    Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
            }
    
            void Add(int k, const int &ql, const int &qr, const int &id){
                    int l = T[k].l, r = T[k].r;
                    if(r < ql || l > qr) return ;
                    if(ql <= l && r <= qr) return T[k].b.pb(id), void();
                    Add(k<<1, ql, qr, id), Add(k<<1|1, ql, qr, id);
            }
    
            void solve(int k){
                    int last = stk.top; bool flag = 0;
                    for(reg int i = T[k].b.size()-1; ~i; i --){
                   //         printf("%d
    ", T[k].b[i]);
                            EDGE t = E[T[k].b[i]];
                            int t1 = Find(t.u), t2 = Find(t.v);
                            if(Rk[t1] > Rk[t2]) std::swap(t1, t2);
                            //std::swap(t.u, t.v);
                            int dsu = ged(t.u), dsv = ged(t.v);
                            if(t1 == t2){ if(!(dsu^dsv)) flag = 1; continue ; }
                            F[t1] = t2, ds[t1] = dsu ^ dsv ^ 1; 
                            if(Rk[t1] == Rk[t2]){
                                    Rk[t2] ++;
                                    stk.b[++ stk.top] = (EDGE){ t1, 1 };
                            }else   stk.b[++ stk.top] = (EDGE){ t1, 0 };
                    }
                    if(!flag){
                            if(T[k].l == T[k].r) return Ans[T[k].l]=1, void();
                            solve(k<<1), solve(k<<1|1);
                    }
                    while(1){
                            if(stk.top == last) break ;
                            int top = stk.top --;
                            int x = stk.b[top].u;
                            ds[x] = 0, Rk[F[x]] -= stk.b[top].v, F[x] = x;
                    }
            }
    
    } seg_t;
    
    int main(){
            freopen("a.in", "r", stdin);
            freopen("a.out", "w", stdout);
            N = read(), M = read(), Tim = read();
            seg_t.Build(1, 1, Tim);
            for(reg int i = 1; i <= M; i ++){
                    E[i].u = read(), E[i].v = read();
                    int l = read() + 1, r = read(); //----------------
                    seg_t.Add(1, l, r, i);
            }
            for(reg int i = 1; i <= N; i ++) F[i] = i;
            seg_t.solve(1);
            for(reg int i = 1; i <= Tim; i ++) puts(Ans[i]?"Yes":"No");
            return 0;
    }
    
  • 相关阅读:
    数据库 封装类CppSQLite3的helloword VC6
    数据库 sqlite 进阶
    数据库 sqlite3_get_table,sqlite3_free_table
    数据库 sqlite3_open,sqlite3_exec,slite3_close
    数据库 SQLite C++ 接口
    数据库 如何在VC6下使用sqlite3
    MFC CButtonST使用技巧(一)(二)(三)
    MFC CButtonST简介
    MFC CButtonST 在你的程序中如何使用CButtonST类
    MFC静态分割后锁定分隔条/限制分隔条的移动范围 方法1
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822438.html
Copyright © 2020-2023  润新知