BZOJ4025 二分图 [线段树分治]

二分图

题目描述见链接 .


\color{red}{正解部分}

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

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

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

再考虑如何维护边的存在, 使用 线段树分治,
每条边都会有一个出现的时间区间 [L,R][L, R], 考虑建立下标为 [1,T][1, T]线段树 ,
将每条边的时间区间 [L,R][L, R] 分成 logT\log 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\n", 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;
}
posted @ 2019-10-09 23:16  XXX_Zbr  阅读(117)  评论(0编辑  收藏  举报