JZOJ4769 【GDOI2017模拟9.9】graph CDQ分治+用按秩合并维护带撤销的并查集(BZOJ 4025)

来源:互联网 发布:剑灵捏脸数据百度云 编辑:程序博客网 时间:2024/04/30 13:17

题目大意

现在有N个点,M个操作,对于每个操作有两种情况
1. Ord=1:读入u,v表示连接u,v
2. Ord=0:读入u表示删除第u次连接的边。
对于每次操作,如果当前是一幅二分图则输出YES,否则输出NO

N,M3105

解题思路

我们先考虑没有删除的情况,判断当前的图是不是一个二分图就相当与判断图中有没有奇环。这个可以用并查集集很轻松的维护(直接记录一下当前点与他祖先是否在二分图的一边,每次找父亲的时候更新一下)。

现在增加了删除操作,我们发现在线很难维护(好像用LCT可以做),那么我们就想想离线的做法。我们可以把第i个操作设为时间i,那么我们就知道每一条边的存在时间。那么对于一个时间段(L,R)我们想要知道对这段区间有影响的边集E,这个可以用CQD分治实现。设(L,R,E)表示时间在区间(L,R),边集为E时二分图的情况(一样用并查集维护),我们只需每次把符合要求的边分到两边就可以实现E的维护。那么现在有一个问题就是我们递归进一个状态后怎么撤销这个时间区间的边的影响。

由于如果并查集加了路径压缩优化后就会丢失中间的信息,所以我们考虑只用按秩合并在LogN的时间内维护并查集,然后对于每个CDQ分治的每个状态记录下这一层对当前二分图的影响,退出时再恢复就可以了。

程序

#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int MAXN = 3e5 + 5;struct Node {    int u, v, l, r;    Node (int a, int b, int c, int d) {u = a, v = b, l = c, r = d;}    Node () {}} D[MAXN];int N, M, tot, Ans[MAXN], Save[MAXN], Cnt, Fa[MAXN], Val[MAXN], Deep[MAXN];int Get(int u) {return (Fa[u] == u) ? u : Get(Fa[u]);}int GetVal(int u) {    int Ans = 0;    for (; u != Fa[u]; u = Fa[u]) Ans ^= Val[u];    return Ans;}void Back(int tag) {    for (; Cnt != tag; Cnt --) {        if (Save[Cnt] < 0) Deep[-Save[Cnt]] --; else {            Fa[Save[Cnt]] = Save[Cnt];            Val[Save[Cnt]] = 0;        }    }}void Push(int u, int v, int val) {    if (Deep[u] > Deep[v]) swap(u, v);    if (Deep[u] == Deep[v]) {        Deep[v] ++;        Save[++ Cnt] = -v;    }    Fa[u] = v, Val[u] = val;    Save[++ Cnt] = u;}void Solve(int L, int R, int Num) {    int tag = Cnt;    for (int i = 1; i <= Num; i ++) {        int u = D[i].u, v = D[i].v;        if (D[i].l <= L && D[i].r >= R) {            int fu = Get(u), fv = Get(v);            int Val = GetVal(u) ^ GetVal(v) ^ 1;            if (fu == fv) {                if (Val) {                    for (int j = L; j <= R; j ++) printf("NO\n");                    Back(tag);                    return;                }            }            Push(fu, fv, Val);            swap(D[i --], D[Num --]);        }    }    if (L == R) {        printf("YES\n");        Back(tag);        return;    }    int Mid = (L + R) >> 1, Lim = Num;    for (int i = 1; i <= Lim; i ++)         if (D[i].l > Mid) swap(D[i --], D[Lim --]);    Solve(L, Mid, Lim);    Lim = Num;    for (int i = 1; i <= Lim; i ++)        if (D[i].r <= Mid) swap(D[i --], D[Lim --]);    Solve(Mid + 1, R, Lim);    Back(tag);}int main() {    scanf("%d%d", &N, &M);    for (int i = 1; i <= M; i ++) {        int Ord;        scanf("%d", &Ord);        if (Ord == 1) {            int u, v;            scanf("%d %d\n", &u, &v);            u ++, v ++;            D[++ tot] = Node(u, v, i, N);        } else {            int id;            scanf("%d\n", &id);            D[++ id].r = i - 1;        }    }    for (int i = 1; i <= N; i ++) Fa[i] = i;    Solve(1, M, tot);}
1 0