bzoj4025 二分图

Posted 逢山开路 遇水架桥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4025 二分图相关的知识,希望对你有一定的参考价值。

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4025

【题解】

考虑对时间分治,用可撤回的启发式合并并查集来维护连通性。

二分图的条件是没有奇环,用并查集判即可。

对于时间区间[l,r],如果边在这期间都存在,那么就加入并查集,对于剩下的边分类,并且分治下去做。

对于每条边,在log个区间表示出来了,需要进行判断,启发式合并的getf是log的,所以复杂度为O(nlog^2n)

技术分享
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10, N = 1e6 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, T;

struct edge {
    int u, v, st, ed;
    edge() {}
    edge(int u, int v, int st, int ed) : u(u), v(v), st(st), ed(ed) {}
}e[M]; 

int st[N];
int stn = 0; 

struct us {
    int n, fa[M], rk[M], dis[M];
    inline void set(int _n) {
         n = _n;
         for (int i=1; i<=n; ++i) fa[i] = i, rk[i] = 1, dis[i] = 0;
    }
    inline int getf(int x) {
        return fa[x] == x ? x : getf(fa[x]); 
    }
    inline int getdis(int x) {
        int d = 0;
        while(fa[x] != x) d += dis[x], x = fa[x]; 
        return d;
    }
    inline void un(int fu, int fv, int d) {
        if(rk[fu] > rk[fv]) swap(fu, fv);
        if(rk[fu] == rk[fv]) rk[fv] ++, st[++stn] = -fv; 
        fa[fu] = fv;
        dis[fu] = d; 
        ++stn;
        st[stn] = fu;
    }
    inline void re() {
        int x = st[stn]; --stn;
        if(x < 0) --rk[-x];
        else dis[x] = 0, fa[x] = x;
    }
}E;
        
bool ans[M]; 

inline void solve(int tl, int tr, int er) {
//    printf("l = %d, r = %d\n", tl, tr); 
    int cur_stn = stn;
    for (int i=1; i<=er; ++i) {
        if(!(e[i].st <= tl && tr <= e[i].ed)) continue; 
//        printf("doing: (%d, %d, %d, %d)\n", e[i].u, e[i].v, e[i].st, e[i].ed); 
        int fu = E.getf(e[i].u), fv = E.getf(e[i].v);
        int du = E.getdis(e[i].u), dv = E.getdis(e[i].v);
        if(fu != fv) E.un(fu, fv, du+dv+1);
        else {
//            printf("%d %d fa = %d %d %d\n", e[i].u, e[i].v, E.fa[1], du, dv); 
            if((du+dv+1)&1) {
//                puts("Proves odd"); 
                while(stn != cur_stn) E.re();
                return ;
            }
        }
        swap(e[i], e[er]); --er; --i; 
    }
    if(tl == tr) {
        ans[tl] = 1;
        while(stn != cur_stn) E.re(); 
        return ;
    }
    int mid = tl+tr >> 1;
    int ER = er;
    for (int i=1; i<=er; ++i) 
        if(e[i].st > mid) {
            swap(e[i], e[er]); 
            --er; --i;
        }
    solve(tl, mid, er);
    er = ER;
    for (int i=1; i<=er; ++i) {
        if(e[i].ed <= mid) {
            swap(e[i], e[er]);
            --er; --i;
        }
    }
    solve(mid+1, tr, er); 
    while(stn != cur_stn) E.re(); 
}

int main() {
    cin >> n >> m >> T;
    E.set(n); 
    int en = 0;
    for (int i=1, u, v, sta, end; i<=m; ++i) {
        scanf("%d%d%d%d", &u, &v, &sta, &end);
        ++sta; if(sta>end) continue;
        e[++en] = edge(u, v, sta, end);
    }
    m = en;
    solve(1, T, m); 
    for (int i=1; i<=T; ++i) puts(ans[i] ? "Yes" : "No"); 
    return 0;
}
View Code

 

以上是关于bzoj4025 二分图的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj4025]二分图_LCT

BZOJ4025 二分图

bzoj4025: 二分图(留坑后填)

BZOJ 4025 二分图(时间树+并查集)

bzoj4025 二分图

[BZOJ4025]二分图(线段树分治,并查集)