Loading

【题解】P5787 二分图 /【模板】线段树分治

概念

线段树分治是一种用于维护时间轴等的离线算法,本质上是通过维护时间轴的连续区间得到某一时刻的状态。

时间复杂度和普通线段树相同,空间复杂度为 \(O(n \log n)\).

现在常见的应用是维护有操作的图连通性问题。

例题

P5787 二分图 /【模板】线段树分治

将每条边看成修改操作,则它的作用范围是时刻区间 \([l, r)\).

考虑对时间轴构造一棵线段树。对于线段树上的结点,假设它代表的区间是 \([l, r]\),则该结点维护所有在 \([l, r]\) 时刻均存在的边。

于是从根到叶结点的路径可以维护某一时刻存在的所有边。

假如可以快速判定二分图,我们只需要对整棵线段树进行一次遍历,动态维护当前的图就行。

判定二分图可以考虑可撤销扩展域并查集。

因为线段树树高为 \(O(\log n)\) 级别,所以每条边至多被分解成 \(O(\log n)\) 个信息,空间复杂度为 \(O(m \log n)\).

复杂度为 \(O(k \log k \log n)\)

代码

#include <cstdio>
#include <vector>
using namespace std;

const int maxn = 1e5 + 5;
const int maxk = 1e5 + 5;
const int dsu_sz = maxn << 1;
const int t_sz = maxk << 2;

typedef pair<int, int> pii;

#define pb push_back
#define mp make_pair
#define swap(x, y) x ^= y ^= x ^= y

int n, m, k;
bool ans[maxk];

namespace DSU
{
    int top, stk[dsu_sz], fa[dsu_sz], sz[dsu_sz];

    void init() { for (int i = 1; i <= (n << 1); i++) fa[i] = i, sz[i] = 1; }

    int get(int x) { return (fa[x] == x ? x : get(fa[x])); }

    void merge(int x, int y)
    {
        x = get(x), y = get(y);
        if (x == y) return;
        if (sz[x] > sz[y]) swap(x, y);
        fa[x] = y, sz[y] += sz[x], stk[++top] = x;
    }

    void undo(int lst)
    {
        while (top > lst)
        {
            int u = stk[top--];
            sz[fa[u]] -= sz[u], fa[u] = u;
        }
    }
}

namespace SGT
{
    #define ls (k << 1)
    #define rs (k << 1 | 1)

    vector<pii> edge[t_sz];

    void update(int k, int l, int r, int ql, int qr, int u, int v)
    {
        if ((l >= ql) && (r <= qr)) return edge[k].pb(mp(u, v)), void();
        int mid = (l + r) >> 1;
        if (ql <= mid) update(ls, l, mid, ql, qr, u, v);
        if (qr > mid) update(rs, mid + 1, r, ql, qr, u, v);
    }

    void query(int k, int l, int r)
    {
        int lst = DSU::top;
        for (pii it : edge[k])
        {
            int u = it.first, v = it.second;
            DSU::merge(u, v + n), DSU::merge(v, u + n);
            if ((DSU::get(u) == DSU::get(u + n)) || (DSU::get(v) == DSU::get(v + n))) return DSU::undo(lst), void();
        }
        if (l == r) ans[l] = true;
        else
        {
            int mid = (l + r) >> 1;
            query(ls, l, mid), query(rs, mid + 1, r);
        }
        DSU::undo(lst);
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    DSU::init();
    for (int i = 1, u, v, l, r; i <= m; i++)
    {
        scanf("%d%d%d%d", &u, &v, &l, &r);
        if (l != r) SGT::update(1, 1, k, l + 1, r, u, v);
    }
    SGT::query(1, 1, k);
    for (int i = 1; i <= k; i++) puts(ans[i] ? "Yes" : "No");
    return 0;
}
posted @ 2023-01-22 20:06  kymru  阅读(48)  评论(0编辑  收藏  举报