洛谷5787 (线段树分治)

例题:有一个 $n$个节点的图,在  $k$时间内有  $m$条边会出现后消失,要求出每一时间段内这个图是否是二分图。

输入格式:第一行三个整数  $n, m, k$接下来 m行,每行四个整数   $x, y, l, r$,表示有一条连接  $x, y$的边在  $l$时刻出现  $r$时刻消失。

输出格式:  $k$行,第  $i$行一个字符串 Yes 或 No,表示在第 i时间段内这个图是否是二分图

分析:在时间轴上建一棵线段树,这样对于每个操作,相当于在线段树上进行区间操作。对于每条边,将它按照线段树区间操作的方式划分成  O(logk)段,用 vector 挂在线段树的节点上。遍历时,从根节点出发,每到一个节点,将挂在该节点上的所有边合并,然后递归处理左儿子和右儿子。如果发现有某条边合并会出现奇环,那么当前线段树节点所对应的时间区间都不会形成二分图。当到达叶子节点时,如果合并了所有挂在当前节点上的边,依旧满足二分图的性质,那么可以直接输出 Yes。回溯时,由于并查集不支持删边,我们可以使用可撤销并查集,即用一个栈记录下所有对并查集的操作。由于可撤销,因此不能路径压缩,为保证复杂度,必须按秩合并。

#include<cstdio>
#include<vector>
#include<stack>
#define fi first
#define se second
#define mid ((l + r) >> 1)
using namespace std;
const int N = 1e5 + 5;
const int M = 2e5 + 5;

typedef pair<int, int> pii;

int n, m, k;
int x[M], y[M], l[M], r[M], fa[N << 1], dep[N];
vector<int> vt[N << 4];
stack<pii> sta;

int find(int u) { return u == fa[u] ? u : find(fa[u]); }
inline void swap(int &x, int &y) { x ^= y ^= x ^= y; }

void ins(int u, int l, int r, int cl, int cr, int id){
    if(cl <= l && cr >= r){
        vt[u].push_back(id);
        return ;
    }
    if(cl <= mid)  ins(u << 1, l, mid, cl, cr, id);
    if(cr > mid)  ins(u << 1 | 1, mid + 1, r, cl, cr, id);
}

void merge(int u, int v){
    if(dep[u] < dep[v])  swap(u, v);
    sta.push(make_pair(v, dep[u] == dep[v]));
    fa[v] = u,  dep[u] += (dep[u] == dep[v]);
}

void solve(int u, int l, int r){
    bool vis = 1;
    int siz = sta.size();
    for(auto &v : vt[u]){
        int k = find(x[v]), kk = find(y[v]);
        if(k == kk){
            vis = 0;
            for(int i = l; i <= r; ++i)  puts("No");
            break;
        }
        merge(find(x[v]), find(y[v] + N)),  merge(find(x[v] + N), find(y[v]));
    }
    if(vis){
        if(l == r)  puts("Yes");
        else{
            solve(u << 1, l, mid);
            solve(u << 1 | 1, mid + 1, r);
        }
    }
    while(sta.size() > siz){
        pii tt = sta.top();  sta.pop();
        dep[fa[tt.fi]] -= tt.second;
        fa[tt.fi] = tt.fi;
    }
}

int main(){
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= m; ++i)
        scanf("%d%d%d%d", &x[i], &y[i], &l[i], &r[i]);
    for(int i = 1; i <= n; ++i)  fa[i] = i, fa[i + N] = i + N;
    for(int i = 1; i <= m; ++i)  if(l[i] != r[i])  ins(1, 1, k, l[i] + 1, r[i], i);
    solve(1, 1, k);
    return 0;
}
你只有十分努力,才能看上去毫不费力。
原文地址:https://www.cnblogs.com/214txdy/p/14024410.html