[CDQ分治][带权并查集]JZOJ 4769

Description

 对于一个图, 如果它的点集能被分成两个部分, 使得在原图中每一部分之间的点没有任何边相连,则该图被称为二分图。
 
    现在给定一个无向图,每次增加一条边,或者删除一条边。要求您每次判断它是不是二分图。
 

Input

 第一行两个数n,m,表示该图的点数和操作数。
        接下来m行,以一个数type开头。type为0或1。若type为1则表示加一条边,接下来输入两个数a,b表示它连接的边的编号,编号从0到n-1;为0则表示删除一条边,接下来是一个数a表示删除加入的第a+1条边。
        保证任何时候图都无重边或自环。

Output

  m行,每行一个"YES"或"NO"(引号不输出),表示每次操作后图是否是二分图。
 

Sample Input

3 3
1 0 1
1 0 2
1 1 2

Sample Output

YES
YES
NO
 

Data Constraint

分析

我们发现其实只是图上的边会在[l,r]的时间内存在罢了

那么我们可以来一波CDQ

用一个vector存边,然后如果跨越当前区间的边就拆到左边或右边的边集L,R中

同时要存一下并查集的状态,不能路径压缩否则无法还原,用按秩合并和栈记录并查集存边顺序,跳出当前层分治时还原并查集

 

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N=3e5+10;
struct Edge {
    int u,v,l,r;
};
vector<Edge> g;
int f[N],r[N],stk[N],d[N];
int n,m,top;
bool ans[N];

int Find(int x) {return f[x]==x?x:Find(f[x]);}

int dist(int x) {return f[x]==x?0:d[x]^dist(f[x]);}

void Merge(int i,int j,int dist) {
    if (r[i]>r[j]) swap(i,j);
    if (r[i]==r[j]) r[j]++,stk[++top]=-j;
    f[i]=j;d[i]=dist;stk[++top]=i;
}

void Pop(int now) {
    for (;top>now;top--) {
        if (stk[top]<0) r[-stk[top]]--;
        else f[stk[top]]=stk[top],d[stk[top]]=0;
    }
}

void Divide(int l,int r,vector<Edge> g) {
    int mid=l+r>>1,now=top;
    vector<Edge> ll,rr;
    for (vector<Edge>::iterator i=g.begin();i!=g.end();i++)
        if (i->l==l&&i->r==r) {
            int u=Find(i->u),v=Find(i->v),d=dist(i->u)^dist(i->v)^1;
            if (u!=v) Merge(u,v,d);
            else if (d&1) {
                for (int j=l;j<=r;j++) ans[j]=1;
                Pop(now);
                return;
            }
        }
        else 
            if (i->r<=mid) ll.push_back(*i);
            else if (i->l>mid) rr.push_back(*i);
            else {
                ll.push_back((Edge){i->u,i->v,i->l,mid});
                rr.push_back((Edge){i->u,i->v,mid+1,i->r});
            }
    if (l!=r) Divide(l,mid,ll),Divide(mid+1,r,rr);
    Pop(now);
}

int main() {
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        int order;
        scanf("%d",&order);
        if (order) {
            int u,v;
            scanf("%d%d",&u,&v);
            g.push_back((Edge){u,v,i,m});
        }
        else {
            int j;
            scanf("%d",&j);
            g[j].r=i-1;
        }
    }
    for (int i=0;i<n;i++) f[i]=i;
    Divide(1,m,g);
    for (int i=1;i<=m;i++) printf(ans[i]?"NO\n":"YES\n");
}
View Code

 

posted @ 2019-03-21 21:37  Vagari  阅读(162)  评论(0编辑  收藏  举报