题面

给一个

N

N

N 点

M

M

M 边的简单无向图,询问

Q

Q

Q 次,每次问你把编号在

[

l

i

,

r

i

]

[l_i,r_i]

[li​,ri​] 之间的边删掉后,该图是否存在奇数环,即是否不能被二染色

1

N

,

M

,

Q

200000

1\leq N,M,Q\leq 200000

1≤N,M,Q≤200000.

题解

看了半天才搞懂官解里的奇怪分治是什么,其实就是整体二分嘛!

部分分就不多赘述了,大概就是一步步引导我们到正解的整体二分+可回退并查集(官解称其为:DSU)上。

并查集是经典的解决二染色问题的方法了,每个点存一个值表示自己和父亲的颜色是否不同(0/1),再存一个集合大小,然后用启发式合并保证每次只变动一条边,最后用一个栈来存每次加的边,以备回退之需。

我们发现答案具有包含性,即:若询问为

[

l

,

r

]

[l,r]

[l,r] 时答案是 YES,那么

[

l

,

r

]

[l',r']

[l′,r′] (

[

l

,

r

]

[

l

,

r

]

[l',r']\sub[l,r]

[l′,r′]⊂[l,r])的答案也是 YES ,这个易证,因为能形成奇数环的边都还在。

再换一种思路,我们令

m

a

x

r

[

i

]

maxr[i]

maxr[i] 为区间左端点 l=i+1满足答案是 YES 的最大右端点编号 +1(即此时

[

1

,

i

]

[

m

a

x

r

[

i

]

,

M

]

[1,i]\cup[maxr[i],M]

[1,i]∪[maxr[i],M] 以内的边都保留)。那么对于一个询问

[

l

,

r

]

[l,r]

[l,r] 当且仅当

m

a

x

r

[

l

1

]

>

r

maxr[l-1]>r

maxr[l−1]>r 时答案为 YES。同时,由于之前的包含性,我们可以发现

i

<

j

m

a

x

r

[

i

]

m

a

x

r

[

j

]

i<j\Rightarrow maxr[i]\leq maxr[j]

i<j⇒maxr[i]≤maxr[j]

即:

m

a

x

r

[

i

]

maxr[i]

maxr[i] 单调不降!

为了配合可回退并查集,具体地,我们这样分治:

  1. s

    o

    l

    v

    e

    (

    l

    1

    ,

    r

    1

    ,

    l

    2

    ,

    r

    2

    )

    solve(l_1,r_1,l_2,r_2)

    solve(l1​,r1​,l2​,r2​),四个参数,分别表示两个区间:要求的标号区间

    [

    l

    1

    ,

    r

    1

    ]

    [l_1,r_1]

    [l1​,r1​],以及通过之前的计算已经确定的该区间内针对所有位置

    m

    a

    x

    r

    maxr

    maxr 的范围

    [

    l

    2

    ,

    r

    2

    ]

    [l_2,r_2]

    [l2​,r2​]。为配合并查集,操作前得保证前提条件

    l

    1

    l_1

    l1​ 左边的边和

    r

    2

    r_2

    r2​ 右边的边都已经加入并查集。

  2. 整体二分常规程序,先取

    m

    i

    d

    1

    =

    l

    1

    +

    r

    1

    2

    mid_1=\lfloor\frac{l_1+r_1}{2}\rfloor

    mid1​=⌊2l1​+r1​​⌋,然后把

    [

    l

    1

    ,

    m

    i

    d

    1

    ]

    [l_1,mid_1]

    [l1​,mid1​] 的边都加上,再从

    r

    2

    r_2

    r2​ 开始往前加边,直到存在奇数环,即求出了

    m

    a

    x

    r

    [

    m

    i

    d

    1

    ]

    maxr[mid_1]

    maxr[mid1​]。

  3. m

    i

    d

    2

    =

    m

    a

    x

    r

    [

    m

    i

    d

    1

    ]

    mid_2=maxr[mid_1]

    mid2​=maxr[mid1​],那么我们可以往下转移了,我们知道

    [

    l

    1

    ,

    m

    i

    d

    1

    1

    ]

    [l_1,mid_1-1]

    [l1​,mid1​−1] 的

    m

    a

    x

    r

    maxr

    maxr 一定在

    [

    l

    2

    ,

    m

    i

    d

    2

    ]

    [l_2,mid_2]

    [l2​,mid2​] 以内,以及

    [

    m

    i

    d

    1

    +

    1

    ,

    r

    1

    ]

    [mid_1+1,r_1]

    [mid1​+1,r1​] 的

    m

    a

    x

    r

    maxr

    maxr 一定在

    [

    m

    i

    d

    2

    ,

    r

    2

    ]

    [mid_2,r_2]

    [mid2​,r2​] 以内。

  4. 递归

    s

    o

    l

    v

    e

    (

    m

    i

    d

    1

    +

    1

    ,

    r

    1

    ,

    m

    i

    d

    2

    ,

    r

    2

    )

    solve(mid_1+1,r_1,mid_2,r_2)

    solve(mid1​+1,r1​,mid2​,r2​) 。此前暴力回退、加边使之满足前提条件

  5. 递归

    s

    o

    l

    v

    e

    (

    l

    1

    ,

    m

    i

    d

    1

    1

    ,

    l

    2

    ,

    m

    i

    d

    2

    )

    solve(l_1,mid_1-1,l_2,mid_2)

    solve(l1​,mid1​−1,l2​,mid2​)。同理。

递归到边界就不用多说了吧。

时间复杂度

O

(

M

log

2

M

+

Q

)

O(M\log^2M+Q)

O(Mlog2M+Q)。

CODE

#include<set>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
int U[MAXN],V[MAXN];
vector<int> g[MAXN];
int fa[MAXN],ds[MAXN],siz[MAXN];
int findd(int x) {if(x == fa[x]) return 0;return findd(fa[x])^ds[x];}
int findf(int x) {return x==fa[x] ? x:findf(fa[x]);}
vector<int> st;
bool unionSet(int a,int b) {
int u = findf(a),v = findf(b);
if(siz[u] > siz[v]) swap(u,v),swap(a,b);
int d1 = findd(a),d2 = findd(b);
if(u == v) {
return d1 ^ d2;
}
if(d1 == d2) ds[u] = 1;
else ds[u] = 0;
siz[v] += siz[u]; fa[u] = v;
st.push_back(u);
return 1;
}
void POP() {
int u = st.back(); st.pop_back();
int v = fa[u];
ds[u] = 0; siz[v] -= siz[u];
fa[u] = u; return ;
}
int maxr[MAXN];
int li[MAXN],ri[MAXN];
void solve(int l,int r,int l2,int r2) {
if(l > r || l2 > r2) return ;
if(l2 == r2) {
for(int i = l;i <= r;i ++) maxr[i] = l2;
return ;
}
int mid = (l + r) >> 1,le = (int)st.size(),rr = max(l2,mid-1);
int flag = 0;
for(int i = l;i <= mid;i ++) if(i) flag |= 1^unionSet(U[i],V[i]);
int le2 = (int)st.size();
if(flag) rr = r2;
for(int i = min(r2,m);i > mid && i >= l2 && i > rr;i --) {
flag |= 1^unionSet(U[i],V[i]);
if(flag) {rr = i;break;}
}
maxr[mid] = rr;
while((int)st.size() > le2) POP();
solve(mid+1,r,rr,r2);
while((int)st.size() > le) POP();
for(int i = min(r2,m);i >= rr;i --) unionSet(U[i],V[i]);
solve(l,mid-1,l2,rr);
while((int)st.size() > le) POP();
return ;
}
int main() {
n = read();m = read();int Q = read();
for(int i = 1;i <= m;i ++) {
s = read();o = read();
g[s].push_back(o);
g[o].push_back(s);
U[i] = s; V[i] = o;
}
int rr = 0;
for(int i = 1;i <= Q;i ++) li[i] = read(),ri[i] = read(),rr = max(li[i],rr);
for(int i = 1;i <= n;i ++) fa[i] = i,ds[i] = 0,siz[i] = 1;
solve(0,m,0,m+1);
for(int i = 1;i <= Q;i ++) {
s = li[i];o = ri[i];
if(maxr[s-1] > o) printf("YES\n");
else printf("NO\n");
}
return 0;
}

[CF1386C] Joker (IOI 赛制,分治,整体二分+可回退并查集)的更多相关文章

  1. CQD(陈丹琦)分治 & 整体二分——专题小结

    整体二分和CDQ分治 有一些问题很多时间都坑在斜率和凸壳上了么--感觉斜率和凸壳各种搞不懂-- 整体二分 整体二分的资料好像不是很多,我在网上找到了一篇不错的资料:       整体二分是个很神的东西 ...

  2. 一篇自己都看不懂的CDQ分治&整体二分学习笔记

    作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...

  3. Cdq分治整体二分学习记录

    这点东西前前后后拖了好几个星期才学会……还是自己太菜啊. Cdq分治的思想是:把问题序列分割成左右两个,先单独处理左边,再处理左边对右边的影响,再单独处理右边.这样可以消去数据结构上的一个log,降低 ...

  4. [学习笔记] CDQ分治&整体二分

    突然诈尸.png 这两个东西好像都是离线骗分大法... 不过其实这两个东西并不是一样的... 虽然代码长得比较像 CDQ分治 基本思想 其实CDQ分治的基本思想挺简单的... 大概思路就是长这样的: ...

  5. 算法笔记--CDQ分治 && 整体二分

    参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 前置技能:树 ...

  6. CDQ分治&整体二分学习个人小结

    目录 小结 CDQ分治 二维LIS 第一道裸题 bzoj1176 Mokia bzoj3262 陌上花开 bzoj 1790 矩形藏宝地 hdu5126四维偏序 P3157 [CQOI2011]动态逆 ...

  7. luogu P5473 [NOI2019]I 君的探险 交互 随机 二分 分治 整体二分

    LINK:I 君的探险 神仙题! 考虑一个暴力的做法 每次点亮一个点 询问全部点 这样询问次数为 \(\frac{n\cdot (n-1)}{2}\) 可以通过前5个点. 考虑都为A的部分分 发现一个 ...

  8. Codeforces 938G Shortest Path Queries [分治,线性基,并查集]

    洛谷 Codeforces 分治的题目,或者说分治的思想,是非常灵活多变的. 所以对我这种智商低的选手特别不友好 脑子不好使怎么办?多做题吧-- 前置知识 线性基是你必须会的,不然这题不可做. 推荐再 ...

  9. 线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

    闲话 stO猫锟学长,满脑子神仙DS 网上有不少Dalao把线段树分治也归入CDQ分治? 还是听听YCB巨佬的介绍: 狭义:只计算左边对右边的贡献. 广义:只计算外部对内部的贡献. 看来可以理解为广义 ...

随机推荐

  1. centos 修改PHP默认版本

    命令行输入export PATH=/usr/local/php/bin:$PATH 然后回车.

  2. SmartIDE v0.1.19 - 码云(Gitee)最有价值开源项目奖项、工作区策略、类虚拟机镜像VMLC、Server安装手册

    SmartIDE v0.1.19 (CLI Build 3909, Server Build 3890) 已经发布,本次Sprint主要完成2个重要特性,工作区策略和类虚拟机容器(VM Like Co ...

  3. Python基础学习笔记_02

    Python中的运算符 标准算术运算符 加(+) 减(-) 乘(*) 除(/) 整除(//) print(1+1) #加法运算 print(1-1) #减法运算 print(2*4) #乘法运算 pr ...

  4. 好用到爆!GitHub 星标 32.5k+的命令行软件管理神器,功能真心强大!

    前言(废话) 本来打算在公司偷偷摸摸给星球的用户写一篇编程喵整合 MongoDB 的文章,结果在通过 brew 安装 MongoDB 的时候竟然报错了.原因很简单,公司这台 Mac 上的 homebr ...

  5. salt stack安装与使用

    SaltStack除了传统的C/S架构外,其实还有Masterless架构,如果采用Masterless架构,我不需要单独安装一台SaltStack Master机器,只需要在每台机器上安装Minio ...

  6. 国外价值10K+美金的Python面试题,珍藏已久,含泪放了出来

    兄弟们,没吹牛皮,一哥们在国外面试的时候,就是要他做的这个,直接给他说,做出来了给你15K(单位是刀),做不出来就拜拜~ 大兄弟当时就不服了,这不是看不起我么,分分钟就给整完了~ 我上我也行系列: 唠 ...

  7. 来用python自己做一个闹钟吧

    闹钟 是一种具有可以在预先设定的时间被激活以响铃的功能的时钟,用于唤醒打工人们. 使用Python中的DateTime模块来创建闹钟,并用Python中的playsound库来播放闹钟声音.~~~## ...

  8. 毕设着急了吧?Python股票数据分析,制作动态柱状图

    写在前面的一些屁话: 雪球成立于 2010 年,是北京雪球信息科技有限公司旗下推出的投资者社区.雪球一直致力于为中国投资者提供跨市场(沪深.香港.美国),跨品种(股票.基金.债券等)的数据查询.资讯获 ...

  9. 【小程序自动化Minium】二、元素定位-Page接口中的 get_element() 与 get_elements()

    UI自动化中的重要工作就是元素定位了,高效精准的定位方法可以让工作事半功倍. 在过去的一段web自动化经历中,使用的selenium库支持了多种定位方法,我们可以利用这些定位方法来做进一步封装,写出符 ...

  10. idea启动java Maven项目,出现" java: 程序包xxxx不存在"

    解决办法如下:将idea的构建和运行托管到maven下面