LOJ #121. 「离线可过」动态图连通性 LCT维护最大生成树
这个还是比较好理解的.
你考虑如果所有边构成一棵树的话直接用 LCT 模拟一波操作就行.
但是可能会出现环,于是我们就将插入/删除操作按照时间排序,然后依次进行.
那么,我们就要对我们维护的生成树改变一下定义:生成树中的每一条边都是关键边,且要求两点间关键边的最小值最大.
什么边能成为关键边?就是这个边要是在当前时刻被删掉的话这个图就不可能联通.
而一条边在插入时如果两个端点不连通,显然是关键边,而如果联通,则替换掉两点路径中结束时间最早的那个边.
那么新加入的边就成为了关键边,之前那个边就没有用了.
#include <bits/stdc++.h>
#define N 50002
#define M 500005
#define inf 1000000000
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout)
using namespace std;
struct Link_Cut_Tree
{
#define lson t[x].ch[0]
#define rson t[x].ch[1]
int sta[N+M];
struct Node
{
int ch[2],f,rev,min,id,val;
}t[M+N];
int get(int x)
{
return t[t[x].f].ch[1]==x;
}
int isrt(int x)
{
return !(t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x);
}
void pushup(int x)
{
t[x].id=x;
t[x].min=t[x].val;
if(lson&&t[lson].min<t[x].min)
{
t[x].min=t[lson].min;
t[x].id=t[lson].id;
}
if(rson&&t[rson].min<t[x].min)
{
t[x].min=t[rson].min;
t[x].id=t[rson].id;
}
}
void mark(int x)
{
if(x) t[x].rev^=1,swap(lson,rson);
}
void pushdown(int x)
{
if(x&&t[x].rev)
{
t[x].rev=0;
if(lson) mark(lson);
if(rson) mark(rson);
}
}
void rotate(int x)
{
int old=t[x].f,fold=t[old].f,which=get(x);
if(!isrt(old)) t[fold].ch[t[fold].ch[1]==old]=x;
t[old].ch[which]=t[x].ch[which^1],t[t[old].ch[which]].f=old;
t[x].ch[which^1]=old,t[old].f=x,t[x].f=fold;
pushup(old),pushup(x);
}
void splay(int x)
{
int u=x,v=0,fa;
for(sta[++v]=u;!isrt(u);u=t[u].f) sta[++v]=t[u].f;
for(;v;--v) pushdown(sta[v]);
for(u=t[u].f;(fa=t[x].f)!=u;rotate(x))
{
if(t[fa].f!=u)
rotate(get(fa)==get(x)?fa:x);
}
}
void Access(int x)
{
for(int y=0;x;y=x,x=t[x].f)
splay(x),rson=y,pushup(x);
}
void makeroot(int x)
{
Access(x),splay(x),mark(x);
}
void split(int x,int y)
{
makeroot(x),Access(y),splay(y);
}
int findroot(int x)
{
Access(x),splay(x);
for(;lson;) x=lson;
return x;
}
void link(int x,int y)
{
makeroot(x),t[x].f=y;
}
void cut(int x,int y)
{
makeroot(x),Access(y),splay(y);
t[y].ch[0]=t[x].f=0;
pushup(y);
}
#undef lson
#undef rson
}lct;
int n,m,cnt,is[M],U[M],V[M],vised[M];
map<int,int>lst[N];
struct Edge
{
int u,v,s,t;
Edge(int u=0,int v=0,int s=0,int t=0):u(u),v(v),s(s),t(t){}
}e[M];
vector<int>Add[M],Del[M];
void Delete_edge(int id)
{
if(vised[id]) return;
int u=e[id].u;
int v=e[id].v;
int now=id+n;
lct.cut(now, u);
lct.cut(now, v);
}
void Add_edge(int id)
{
int u=e[id].u;
int v=e[id].v;
int now=id+n;
if(lct.findroot(u)==lct.findroot(v))
{
lct.split(u,v);
if(lct.t[v].min<e[id].t)
{
int cur=lct.t[v].id;
vised[cur-n]=1;
lct.cut(cur, e[cur-n].u);
lct.cut(cur, e[cur-n].v);
lct.t[now].val=e[id].t;
lct.pushup(now);
lct.link(u,now);
lct.link(v,now);
}
else vised[id]=1;
}
else
{
lct.t[now].val=e[id].t;
lct.pushup(now);
lct.link(now,u);
lct.link(now,v);
}
}
int main()
{
int i,j;
// setIO("input");
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
int op,u,v,pre;
scanf("%d%d%d",&op,&u,&v);
if(op==2) is[i]=1,U[i]=u,V[i]=v;
else
{
if(op==0)
{
e[++cnt]=Edge(u,v,i,m+1);
lst[u][v]=lst[v][u]=cnt;
Add[i].push_back(cnt);
}
else
{
pre=lst[u][v];
e[pre].t=i;
Del[i].push_back(pre);
lst[u][v]=lst[v][u]=0;
}
}
}
for(i=1;i<=n;++i)
{
lct.t[i].val=inf;
lct.pushup(i);
}
for(i=1;i<=m;++i)
{
for(j=0;j<Add[i].size();++j) Add_edge(Add[i][j]);
for(j=0;j<Del[i].size();++j) Delete_edge(Del[i][j]);
if(is[i]) printf("%c\n",lct.findroot(U[i])==lct.findroot(V[i])?'Y':'N');
}
return 0;
}
LOJ #121. 「离线可过」动态图连通性 LCT维护最大生成树的更多相关文章
- LOJ 121 「离线可过」动态图连通性——LCT维护删除时间最大生成树 / 线段树分治
题目:https://loj.ac/problem/121 离线,LCT维护删除时间最大生成树即可.注意没有被删的边的删除时间是 m+1 . 回收删掉的边的节点的话,空间就可以只开 n*2 了. #i ...
- LOJ#121. 「离线可过」动态图连通性(线段树分治)
题意 板子题,题意很清楚吧.. Sol 很显然可以直接上LCT.. 但是这题允许离线,于是就有了一个非常巧妙的离线的做法,好像叫什么线段树分治?? 此题中每条边出现的位置都可以看做是一段区间. 我们用 ...
- loj#121.「离线可过」动态图连通性
题面 话说#122怎么做啊 题解 我的\(\mathrm{LCT}\)水平极差,连最小生成树都快忘了,赶紧复习一下 做法和这篇是一样的 这道题还可以练习线段树分治 还可以练习ETT 果然是道吼题 代码 ...
- 【LOJ】#121. 「离线可过」动态图连通性
题解 和BZOJ4025挺像的 就是维护边权是时间的最大生成树 删边直接删 两点未联通时直接相连,两点联通则找两点间边权小的一条边删除即可 代码 #include <bits/stdc++.h& ...
- 【LOJ121】「离线可过」动态图连通性
[LOJ121]「离线可过」动态图连通性 题面 LOJ 题解 线段树分治的经典应用 可以发现每个边出现的时间是一个区间 而我们每个询问是一个点 所以我们将所有边的区间打到一颗线段树上面去 询问每个叶子 ...
- LOJ121 「离线可过」动态图连通性
思路 动态图连通性的板子,可惜我不会在线算法 离线可以使用线段树分治,每个边按照存在的时间插入线段树的对应节点中,最后再dfs一下求出解即可,注意并查集按秩合并可以支持撤销操作 由于大量使用STL跑的 ...
- 「LOJ 121」「离线可过」动态图连通性「按时间分治 」「并查集」
题意 你要维护一张\(n\)个点的无向简单图.你被要求执行\(m\)条操作,加入删除一条边及查询两个点是否连通. 0:加入一条边.保证它不存在. 1:删除一条边.保证它存在. 2:查询两个点是否联通. ...
- LOJ 546: 「LibreOJ β Round #7」网格图
题目传送门:LOJ #546. 题意简述: 题目说的很清楚了. 题解: 将不包含起点或障碍物的连续的行或列缩成一行或一列,不会影响答案. 处理过后,新的网格图的行数和列数最多为 \(2k + 3\). ...
- LOJ121 【离线可过】动态图连通性
题目链接:戳我 [线段树分治版本代码] 这里面的线段树是时间线段树,每一个节点都要开一个vector,记录当前时间区间中存在的边的标号qwq #include<iostream> #inc ...
随机推荐
- Python 同级目录import报错
在使用protobuf时,我们自己了各种Options的时候,在生成的python文件会在同级目录中引用,所以这个时候我们不能做到,加个__init__.py文件了事然后在文件里面写 import m ...
- 创客课堂——Scratch的操作界面
大家好,这里是蓝精灵创客公益课堂,我是蓝老师 上期我们了解了scratch软件进行了简单的了解,很多朋友都已经按照上期方法下载安装好了软件. 那么今天蓝老师就和大家一起认识下Scratch的操作界面及 ...
- BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)
二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...
- Shell脚本基础学习
Shell脚本基础学习 当你在类Unix机器上编程时, 或者参与大型项目如k8s等, 某些框架和软件的安装都是使用shell脚本写的. 学会基本的shell脚本使用, 让你走上人生巅峰, 才怪. 学会 ...
- Windows 10 下 Linux 子系统的安装和使用
介绍 适用于 Windows 的 Linux 子系统(英语:Windows Subsystem for Linux,简称 WSL)是一个为在 Windows 10 和 Windows Server 2 ...
- 怎么制作电脑系统安装U盘?
现如今U盘安装电脑系统已经是非常普遍的一种方式,这种方式简单好用,能应对大多数情况,受到很多用户的欢迎. 雨后清风U盘启动是一款可将普通U盘制作为系统引导启动工具的软件,其制作的U盘启动盘融合了雨后清 ...
- 异常-Exception in thread "main" net.sf.jsqlparser.parser.TokenMgrError: Lexical error at line 1, column 596. Encountered: <EOF> after :
1 详细异常 Exception in thread "main" net.sf.jsqlparser.parser.TokenMgrError: Lexical error at ...
- Ubuntu 手动挂载exfat格式的U盘
1.默认Ubuntu不支持exFat格式的U盘,先要安装支持: sudo apt-get install exfat-fuse 2.挂载磁盘,我选择挂在mnt下面 a.创建挂载目录:sudo mkdi ...
- 【转】xshell 5评估期已过,不能访问的解决方案
原文:https://blog.csdn.net/u010826617/article/details/79165540 重新安装Home & school 版本,原来访问的服务器信息不会丢失 ...
- libcurl 上传文件,不管文件什么格式 libcurl开发指南
libcurl 上传文件,不管文件什么格式 libcurl开发指南 上传文件 https://curl.haxx.se/download.html curl命令参数很多,这里只列出我曾经用过.特别是在 ...