【oSo的题解】题解:P5787 二分图 /【模板】线段树分治
题目:P5787 二分图 /【模板】线段树分治
前言
一个挺简洁巧妙的离线(是的强制在线用不了)小技巧,我竟然看懂了。
题意
给出 \(n\) 个点,\(m\) 条出现时间为 \((l,r]\) 的边,求在 \([1,k]\) 每一时刻,该图是否为二分图。
分析
前置芝士:扩展域并查集(种类并查集)、可撤销并查集(本题不需要可持久化并查集哦)、线段树。
Part-0
首先先说一下如何判断二分图。
我们先开一下每个点的反集,即对于一个点 \(u\),所有与 \(u\) 相连的点 \(v\),都在 \(u+n\) 这个集合里。
然后每加入一条边时,考虑到只有边的两个顶点可能会发生冲突,就 find
一下是否在同一集合中,不在就相互加反集,在就没有必要继续处理了直接往回撤销。
Part-1
那该怎么维护加边和删边呢?
注意到我们不喜欢删边,会造成极大的时间开销,于是可以通过撤销上一次合并操作来达成删边的目的。
而加边就是普通的合并操作。
因为要求我们输出每一时刻的答案,我们可以沿着时间轴来处理询问。
Part-2
如何维护对于某一时间戳上的所有操作?
我们观察到如果只是普通地从前向后扫时间轴,拿到了下一个点及不知道该如何撤销,又不知道该加什么边。
我们可以考虑树形的数据结构。
我们可以对于每一时刻认为是一个独立的叶子节点,然后建线段树。
线段树每一节点都储存着一些操作,这些操作的出现时间均包含该节点的时间段,即相当于每一个节点上的 tag 记录了这一时间段存在的边。
然后我们可以通过类似于区间加的方式,将每一段的出现时间打在线段树上。
Part-3
那我们如何获取答案呢?
观察到,对于每一时刻的答案,即每个叶子的答案,是判断从线段树的根节点到该节点的路径上所有经过点包含的 tag 打在一起的图是否为二分图。
那么我们考虑从根节点进行 dfs,每到一个点就根据 Part-0 判断到目前为止的图是否为二分图,如果不是就没有搜的必要了,可以直接回溯。
代码细节提醒
要注意可撤销并查集只能写按秩合并,不要写路径压缩。
注意当不是二分图需要回溯时一定要先撤销再回溯。
撒花。
AC Code
#include<bits/stdc++.h>
#define int long long
#define rep(I,A,B) for(int I=(A);I<=(B);++I)
#define per(I,A,B) for(int I=(A);I>=(B);--I)
#define el puts("")
#define Yuki return
#define daisuki 0
using namespace std;
using pii=pair<int,int>;
//码风丑还请见谅捏
//代码已经被我分成了小部分,可以按照每个部分进行debug
//不要直接复制粘贴我的代码谢谢哈
//fastIO start
template<typename T>
void read(T &x){
x=0;bool f=0;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=1;
for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+(c^48);
if(f)x=-x;
}
template<typename T,typename ...Args>
void read(T &x,Args &...args){
read(x),read(args...);
}
template<typename T>
void write(T x,char ch){
if(x<0)putchar('-'),x=-x;
static short st[70],tp;
do st[++tp]=x%10,x/=10;while(x);
while(tp)putchar(st[tp--]|48);
putchar(ch);
}
//fastIO end
//some variable start
const int N=1e5+5;
const int M=2e5+5;
int n,m,k;
int fa[N<<1],siz[N<<1],stk[N<<1],tp;
struct Seg{
vector<pii> tag;
}tr[N<<2];
//some variable end
//DS start
//Disjoint Sets Union start
int find(int x){return x==fa[x]?x:find(fa[x]);}//不要写路径压缩捏
void merge(int x,int y){
int p=find(x),q=find(y);
if(p==q)return ;
if(siz[p]<siz[q])swap(p,q);
fa[q]=p;
siz[p]+=q;
stk[++tp]=q;
}
void back(int lst){while(tp>lst){int y=stk[tp--];if(y==fa[y])continue;siz[fa[y]]-=siz[y],fa[y]=y;}}
//Disjoint Sets Union end
//Segment Tree start
void modify(int p,int L,int R,pii k,int l,int r){
if(r<L||l>R)return ;
if(L<=l&&r<=R)return tr[p].tag.emplace_back(k),void();
int mid=l+r>>1;
modify(p<<1,L,R,k,l,mid);
modify(p<<1|1,L,R,k,mid+1,r);
}
void dfs(int p,int l,int r){
int nowtp=tp,mid=l+r>>1;
bool f=0;
for(auto i:tr[p].tag){
int p=find(i.first),q=find(i.second);
if(p==q){
rep(i,l,r)puts("No");f=1;break;
}
merge(i.first,i.second+n),merge(i.first+n,i.second);
}
if(f)return back(nowtp),void();//一定要先back再return
if(l==r)puts("Yes");
else dfs(p<<1,l,mid),dfs(p<<1|1,mid+1,r);
back(nowtp);
}
//Segment Tree end
//DS end
void input(){
read(n,m,k);
rep(i,1,n<<1)fa[i]=i,siz[i]=1;//要注意开反集时要2倍
rep(i,1,m){
int u,v,l,r;
read(u,v,l,r);
modify(1,l+1,r,{u,v},1,k);//区间加边
}
}
void solve(){
dfs(1,1,k);
}
signed main(){
input();
solve();
Yuki daisuki;//有希真的是太可爱了捏
}
//by oSomiwww
【oSo的题解】题解:P5787 二分图 /【模板】线段树分治的更多相关文章
- 【BZOJ4025】二分图(线段树分治,并查集)
[BZOJ4025]二分图(线段树分治,并查集) 题面 BZOJ 题解 是一个二分图,等价于不存在奇环. 那么直接线段树分治,用并查集维护到达根节点的距离,只计算就好了. #include<io ...
- Solution -「洛谷 P5787」「模板」二分图(线段树分治)
\(\mathcal{Description}\) Link. \(n\) 个结点的图,\(m\) 条形如 \((u,v,l,r)\) 的边,表示一条连接 \(u\) 和 \(v\) 的无向 ...
- 【BZOJ4025】 二分图(线段树分治)
传送门 BZOJ Solution 只是为了学习一下线段树分治的啦! 当你学会线段树分治之后,可以跳过下面的一部分: 按照时间搞一颗线段树出来,把包含这段区间的操作用vector压进去. 每一个线段树 ...
- BZOJ4025 二分图(线段树分治+并查集)
之前学了一下线段树分治,这还是第一次写.思想其实挺好理解,即离线后把一个操作影响到的时间段拆成线段树上的区间,并标记永久化.之后一块处理,对于某个节点表示的时间段,影响到他的就是该节点一直到线段树根的 ...
- 2018.09.30 bzoj4025: 二分图(线段树分治+并查集)
传送门 线段树分治好题. 这道题实际上有很多不同的做法: cdq分治. lct. - 而我学习了dzyo的线段树分治+并查集写法. 所谓线段树分治就是先把操作分成lognlognlogn个连续不相交的 ...
- BZOJ4025: 二分图【线段树分治】【带撤销的并查集】
Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. Input ...
- [BZOJ4025] 二分图 LCT/(线段树分治+并查集)
4025: 二分图 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2667 Solved: 989[Submit][Status][Discuss] ...
- P5787 二分图 /【模板】线段树分治
\(\text{Solution}\) 线段树分治的模板 对时间分治,线段树下标表示时间 在线段树上处理每条覆盖当前区间的边,对当前的时间区间求答案 小区间的信息可以由大区间一路下来得到,那么答案就是 ...
- 【题解】【LibreOJ Round #6】花团 LOJ 534 时间线段树分治 背包
Prelude 题目链接:萌萌哒传送门(/≧▽≦)/ Solution 如果完全离线的话,可以直接用时间线段树分治来做,复杂度\(O(qv \log q)\). 现在在线了怎么办呢? 这其实是个假在线 ...
- hdu 1754 I Hate It (模板线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others) M ...
随机推荐
- CSP-J/S 2024 游记
因为靠运气成功拿下了 S T2 的 70 分,所以成功混进 NOIP,直接诈尸了. 这个游记前后写了两个月,比我在 luogu 上的那个多补充了一些东西吧,在我看来还是一场意义十分重大的比赛. 省流: ...
- WSL初探
1 简介 WSL( Windows Subsystem for Linux )是微软开发的兼容层,允许在 Windows 10 及更高版本上运行原生Linux二进制文件(如 Ubuntu . Debi ...
- ETLCloud:新一代ETL数据抽取工具的定义与革新
数据集成.数据治理已经成为推动企业数字化转型的核心动力,现在的企业比任何时候都需要一个更为强大的新一代数据集成工具来处理.整合并转化多种数据源. 而ETL(数据提取.转换.加载)作为数据管理的关键步骤 ...
- ICEE-Keyboard- 键盘工作原理与改装原理:扫描GPIO:{X行,Y列}感应点矩阵在按键触发点感应电路{x,y}通过MCU映射到按键字符
ICEE-Keyboard-键盘工作原理: 周期性扫描电路感应点矩阵: 电路感应点矩阵有总共X行与总共Y列的电路感应{电容式,电阻式,开关式}点, 例如总共12行, 总共12列; 则总共有144个键位 ...
- 在没有curl和wget情况下发送HTTP请求
Bash 的 /dev/tcp 功能为用户提供了一个直接的方式,通过 TCP 套接字发送 HTTP 请求,这一功能可以在没有额外工具的情况下执行简单的网络操作. 发送HTTP GET请求 #!/bin ...
- c#运用ZeroMq发布订阅和RPC函数代理的优点结合成一个新的实用的通讯
想用ZeroMq的发布订阅者模式,又不想写一大串switch case? 想用RPC函数代理机制,又想多对多进行通讯? 下面就结合二者的优点重新封装一套通讯模块 一.先写ZeroMq的发布订阅这模式 ...
- 探讨医疗大模型创业CEO面临的行业困境与前景-九五小庞
要点: - 医疗大模型行业发展迅速,但技术仍需完善,尤其在图像处理和文本领域. - 企业主要使用开源模型,通过数据增强和微调进行训练,但在模型架构上无显著改进. - 投资人质疑盈利模式,对医疗大模型公 ...
- 使用fnm安装node,并自定义安装路径
作者:咕魂 时间:2024年6月23日 本教程使用winget对fnm进行安装,主要分两部分,第一步安装fnm,第二步安装nodejs 其中nodejs配置成功后只在powershell中生效 1. ...
- 对比 Excel 表格工具:Spreadsheet compare
https://zhuanlan.zhihu.com/p/701533987 Spreadsheet compare 可以对比excel表格
- postgres基本操作大全
首先切换到postgres用户 su - postgres -- 首先切换到postgres 常识:PG安装完后默认带有postgres库,可以先进入postgres库再去创建新库 1创建用户: po ...