线段树分治

有一类问题,需要支持插入,删除等一些操作。

可能插入比较容易,但是怎么想都不能删除,这时候就需要用到线段树分治了。

具体怎么做?

按操作时间建立一棵线段树。

线段树上每一个节点用 vector 存下一些操作,表示这些操作在这个节点所代表的时间区间生效。

插入完操作后,开始遍历线段树。

进入线段树的一个节点时,加入所有的操作,并在加入操作之前记录下原来的状态(也可以开 vector 记录)。

到根节点时,一个时间点的所有操作都加入完毕,此时可以进行查询操作了。

回溯时要记得还原成未加入操作前的状态。

分析一下复杂度

由于一次操作会在时间线段树上 \(\log n\) 个节点产生贡献,所以复杂度比平常应该是多了一个 \(\log\)

撤销操作一般复杂度都要小于等于插入操作,所以能插入一般都能撤回。

代码

感觉这道例题可能难点在于带权并查集上,这里挂几篇写的比较好的博客吧。

带权并查集1

带权并查集2

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;
const int M = 2e5 + 5;

int n, m, k;

struct edge { int x, y; } ;

int ou[M], od[M], odist[M], top;

namespace UFS {
    int fa[N], d[N], dist[N];
    int find(int u) { return fa[u] == u ? u : find(fa[u]); }
    int getDist(int u) { return fa[u] == u ? 0 : dist[u] ^ getDist(fa[u]); }
    bool merge(int u, int v) {
        int x = find(u), y = find(v);
        if(x == y) return false;
        if(d[x] > d[y]) swap(u, v), swap(x, y);
        ++top;
        ou[top] = x, od[top] = d[y], odist[top] = dist[x];
        dist[x] = getDist(u) ^ getDist(v) ^ 1, fa[x] = y, d[y] += (d[x] == d[y]); 
        return true; 
    }
    void undo() {
        int u = ou[top];
        d[fa[u]] = od[top];
        dist[u] = odist[top];
        fa[u] = u;
        --top;
    }
}

int ll, rr;
edge ce;

namespace SMT {
    #define lch(x) (x << 1)
    #define rch(x) (x << 1 | 1)
    vector<edge> e[N << 2];
    void modify(int u, int l, int r) {
        if(ll <= l && r <= rr) {
            e[u].push_back(ce);
            return void();
        }
        int mid = (l + r) >> 1;
        if(ll <= mid) modify(lch(u), l, mid);
        if(mid < rr) modify(rch(u), mid + 1, r);
    }
    void dfs(int u, int l, int r) {
        int ecnt = 0; 
        bool flag = false; 
        for(size_t i = 0; i < e[u].size(); ++i) {
            int x = e[u][i].x, y = e[u][i].y;
            if(!UFS::merge(x, y)) {
                flag |= (UFS::getDist(x) == UFS::getDist(y)); 
                if(flag) break;
            } else {
                ++ecnt;
            }
        }
        if(flag) for(int i = l; i <= r; ++i) puts("No");
        else if(l == r) puts("Yes");
        else {
            int mid = (l + r) >> 1;
            dfs(lch(u), l, mid);
            dfs(rch(u), mid + 1, r);
        }
        for(int i = 1; i <= ecnt; ++i)
            UFS::undo();
    }
}

int main() {
    scanf("%d %d %d", &n, &m, &k);
    for(int i = 1; i <= n; ++i)
        UFS::fa[i] = i;
    for(int i = 1, x, y, l, r; i <= m; ++i) {
        scanf("%d %d %d %d", &x, &y, &l, &r);
        ce = (edge){x, y};
        ll = l + 1, rr = r;
        if(ll <= rr) SMT::modify(1, 1, k);
    }
    SMT::dfs(1, 1, k);
    return 0;
}

posted @ 2020-05-20 15:01  Lskkkno1  阅读(335)  评论(0)    收藏  举报