BZOJ 4025 二分图 分治+并查集

题目大意:给定一张n个点的图,有m条边,T个时间段,每条边只存在于(st,ed]这些时间段,求每个时间段内这个图是否是二分图

分治并查集大法好

定义Solve(x,y,E)为当前处理的区间为[x,y],E为所有存在时间为[x,y]的子集的边的集合

那么对于E中的每一条边(u,v),讨论:

若当前边的存在时间为[x,y],则在并查集上判断是否出现奇环

如果出现,[x,y]内的所有时刻都一定不是二分图,输出答案即可

如果不出现,在并查集中连接(u,v)

否则判断存在时间和mid的关系讨论扔进左区间还是右区间还是都扔进去

并查集不需要可持久化,只需要记录进行过的操作,在回溯的时候复原即可

注意并查集不要写路径压缩,因为路径压缩的优化是均摊的,均摊在分治这种树形结构上使用是无效的,只写按秩合并就行了

时间复杂度O(mlog2n)

然而跑的比O(mlogn)的LCT还快是什么鬼……

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;

struct edge{
    int x,y;
    int st,ed;
    edge() {}
    edge(int _,int __,int ___,int ____):
        x(_),y(__),st(___),ed(____) {}
};

int n,m,T;
int stack[M<<2],top;
//若x<0 代表x的rank被加了1
//若x>0 表示x的父亲被修改了 

namespace Union_Find_Set{
    int fa[M],rank[M],a[M];
    int Find(int x)
    {
        while(fa[x]!=x)
            x=fa[x];
        return fa[x]=x;
    }
    int Distance(int x)
    {
        int re=0;
        while(fa[x]!=x&&fa[x])
            re^=a[x],x=fa[x];
        return re;
    }
    void Union(int x,int y,int z)
    {
        x=Find(x);y=Find(y);
        if(x==y) return ;
        if(rank[x]>rank[y])
            swap(x,y);
        if(rank[x]==rank[y])
            rank[y]++,stack[++top]=-y;
        fa[x]=y;a[x]=z;stack[++top]=x;
    }
    void Restore(int bottom)
    {
        while(top>bottom)
        {
            if(stack[top]<0)
                rank[-stack[top]]--;
            else
                fa[stack[top]]=stack[top],a[stack[top]]=0;
            top--;
        }
    }
}

void Divid_And_Conquer(int x,int y,vector<edge> &e)
{
    using namespace Union_Find_Set;
    vector<edge>::iterator it;
    int i,mid=x+y>>1,bottom=top;
    vector<edge> l,r;
    for(it=e.begin();it!=e.end();it++)
    {
        if(it->st==x&&it->ed==y)
        {
            int _x=Find(it->x);
            int _y=Find(it->y);
            int _z=Distance(it->x)^Distance(it->y)^1;
            if(_x!=_y)
                Union(_x,_y,_z);
            else if(_z&1)
            {
                for(i=x;i<=y;i++)
                    puts("No");
                Restore(bottom);
                return ;
            }
        }
        else if(it->ed<=mid)
            l.push_back(*it);
        else if(it->st>mid)
            r.push_back(*it);
        else
            l.push_back(edge(it->x,it->y,it->st,mid)),r.push_back(edge(it->x,it->y,mid+1,it->ed));
    }
    if(x==y)
        puts("Yes");
    else
        Divid_And_Conquer(x,mid,l),Divid_And_Conquer(mid+1,y,r);
    Restore(bottom);
}

int main()
{
    //freopen("4025.in","r",stdin);
    //freopen("4025.out","w",stdout);
    int i;
    edge e;
    vector<edge> v;
    cin>>n>>m>>T;
    for(i=1;i<=n;i++)
        Union_Find_Set::fa[i]=i;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&e.x,&e.y,&e.st,&e.ed);
        e.st++;
        if(e.st>e.ed)
            continue;
        v.push_back(e);
    }
    Divid_And_Conquer(1,T,v);
    return 0;
}
时间: 2024-10-24 22:36:05

BZOJ 4025 二分图 分治+并查集的相关文章

BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集

原文链接http://www.cnblogs.com/zhouzhendong/p/8683831.html 题目传送门 - BZOJ4025 题意 有$n$个点,有$m$条边.有$T$个时间段.其中第$i$条边连接节点$x_i,y_i$,并且在$start_i$时刻出现,在$end_i$时刻消失.问每一个时刻的图是不是二分图. $n\leq 10^5,m\leq 2\times 10^5,T\leq 10^5$ 题解 真是一道好题. 做这题我才发现我从来没写过按秩合并的并查集QAQ. 先考虑按

[hdu 5354] Bipartite Graph 分治 并查集

题意 给定一张 $n$ 个点, $m$ 条边的无向图. 问删去每个点后, 原图是不是二分图. $1 \le n, m \le {10} ^ 5$ . 分析 一个图是二分图 $\Leftrightarrow$ 图中不存在奇环. 判定一个图是不是二分图, 可以使用并查集, 多维护一个当前点与父亲的关系的量 bond . 删除每一个点, 我们有两种维度: 区间加法, 区间减法. 这里考虑区间加法, 即考虑分治. 由于要支持撤销, 所以使用按秩合并的并查集. 注意按照大小合并... 按深度合并会 TLE

【openjudge】C15C Rabbit&#39;s Festival CDQ分治+并查集

题目链接:http://poj.openjudge.cn/practice/C15C/ 题意:n 点 m 边 k 天.每条边在某一天会消失(仅仅那一天消失).问每一天有多少对点可以相互到达. 解法:开始不会做,参考的YYN的题解:http://blog.csdn.net/u013368721/article/details/45725181 学习了这种CDQ加并查集的做法,可以说是非常的巧妙了.复杂度可以保证在:O(KlogklogK)的范围. //CDQ + DSU #include <bit

HDU-3081-Marriage Match II 二分图匹配+并查集 OR 二分+最大流

二分+最大流: 1 //题目大意:有编号为1~n的女生和1~n的男生配对 2 // 3 //首先输入m组,a,b表示编号为a的女生没有和编号为b的男生吵过架 4 // 5 //然后输入f组,c,d表示编号为c的女生和编号为d的女生是朋友 6 // 7 //进行配对的要求满足其一即可. 8 //1.a女生没有和b男生吵过架 9 //2.a女生的朋友和b男生没有吵过架 10 // 11 //每进行一轮之后重新配对,配过得一对不可再配,问最多能进行几轮. 12 // 13 //题解: 14 //这一道

BZOJ 4025 二分图(时间树+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4025 [题目大意] 给出一张图,有些边只存在一段时间,问在一个每个时间段, 这张图是否是二分图 [题解] 判断是否是二分图只要判断是否存在奇环即可, 我们对时间进行分治,在操作树上加删边, 保留涵盖时间区间的有效操作,将剩余操作按时间划分到两端的子树, 退出子树的时候撤销加边操作. 对于判断奇环,我们用并查集维护每个点与标兵的相对距离的奇偶性即可, 由于需要撤销操作,我们放弃对并查集

[BZOJ4025]二分图(线段树分治,并查集)

4025: 二分图 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2191  Solved: 800[Submit][Status][Discuss] Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. Input 输入数据的第一行是三个整数n,m,T. 第2行到第m+1行,每行4个整数u,v,start,end

[CDQ分治 并查集] BZOJ 3237 [Ahoi2013]连通图

考虑CDQ分治 把这半边对后半边没有影响的操作做了 然后分治 用并查集维护 开个栈暴力还原 #include<cstdio> #include<cstdlib> using namespace std; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } re

BZOJ 1854 SCOI2010 游戏 二分图最大匹配/并查集

题目大意:给定n个武器,每个武器有两个属性,只能使用其中一个,要求选择一些武器 可以造成形如1 2 3 4的伤害 求最大伤害 题目大意我没写明白还是去看原题把QAQ 做法1: 同 1191 每个武器向两个属性连边 然后从1~10000枚举属性 跑二分图最大匹配 无法匹配则输出答案 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 100100

bzoj 1854: [Scoi2010]游戏 (并查集||二分图最大匹配)

链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1854 写法1: 二分图最大匹配 思路:  将武器的属性对武器编号建边,因为只有10000种属性,我们直接对1-10000跑二分图匹配,同时用时间戳优化匹配. 实现代码: #include<bits/stdc++.h> using namespace std; const int M = 1e6+10; vector<int>g[M]; int vis[M],pre[M],t