题目:https://loj.ac/problem/121

离线,LCT维护删除时间最大生成树即可。注意没有被删的边的删除时间是 m+1 。

回收删掉的边的节点的话,空间就可以只开 n*2 了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define mkp make_pair
#define ls c[x][0]
#define rs c[x][1]
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e4+,M=5e5+;
int n,m,tot,op[M],fa[N],c[N][],sta[N],top;
int del_pl[N],del_top;
bool rev[N];
map<pair<int,int>,int> mp;
struct Node{int x,y,t;}a[M];
struct Dt{
int t,id,bh;
Dt(int a=,int b=,int c=):t(a),id(b),bh(c) {}
}vl[N],mn[N];
Dt Mn(Dt x,Dt y)
{
if(!x.t)return y; if(!y.t)return x;
return (x.t<y.t)?x:y;
}
int New()
{
int ret=(del_top?del_pl[del_top--]:++tot);
c[ret][]=c[ret][]=fa[ret]=; return ret;
}
bool isroot(int x){return c[fa[x]][]!=x&&c[fa[x]][]!=x;}
void pshp(int x){mn[x]=Mn(vl[x],Mn(mn[ls],mn[rs]));}
void Rev(int x)
{
if(!rev[x])return; rev[x]=;
rev[ls]^=; rev[rs]^=; swap(ls,rs);
}
void rotate(int x)
{
int y=fa[x],z=fa[y],d=(x==c[y][]);
if(!isroot(y))c[z][y==c[z][]]=x;
fa[x]=z;
fa[y]=x; fa[c[x][!d]]=y;
c[y][d]=c[x][!d]; c[x][!d]=y;
pshp(y); pshp(x);
}
void splay(int x)
{
sta[top=]=x;
for(int k=x;!isroot(k);k=fa[k])sta[++top]=fa[k];
for(int i=top;i;i--)Rev(sta[i]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
((x==c[y][])^(y==c[z][]))?rotate(x):rotate(y);
rotate(x);
}
}
void access(int x)
{
for(int t=;x;rs=t,pshp(x),t=x,x=fa[x])splay(x);
}
void mkrt(int x)
{
access(x);splay(x);rev[x]^=;
}
void link(int x,int y)
{
mkrt(x); fa[x]=y;
}
void cut(int x,int y)
{
mkrt(x); access(y); splay(y);
c[y][]=; pshp(y); fa[x]=;
}
bool chk(int x,int y)
{
mkrt(x); access(y); splay(y);
while(c[y][])y=c[y][];
return x==y;
}
void ins(Node cr,int id)
{
int x=cr.x,y=cr.y;
if(chk(x,y))
{
splay(x);
Dt tp=mn[x]; if(tp.t>cr.t)return;
Node k=a[tp.id]; int bh=tp.bh;
cut(k.x,bh); cut(k.y,bh); del_pl[++del_top]=bh;
}
int nw=New(); mn[nw]=vl[nw]=Dt(cr.t,id,nw);
link(cr.x,nw); link(cr.y,nw);
}
int qry(int x,int y)
{
if(!chk(x,y))return ;
splay(x); return mn[x].t;
}
int main()
{
n=rdn();m=rdn();
for(int i=;i<=n;i++)mn[i]=vl[i]=Dt(,,); tot=n;
for(int i=;i<=m;i++)
{
op[i]=rdn();int x=rdn(),y=rdn();if(x>y)swap(x,y);//
if(op[i]==)mp[mkp(x,y)]=i,a[i].x=x,a[i].y=y,a[i].t=m+;//m+1
else if(op[i]==)a[mp[mkp(x,y)]].t=i;
else a[i].x=x,a[i].y=y;
}
for(int i=;i<=m;i++)
{
if(op[i]==)ins(a[i],i);
if(op[i]==)puts(qry(a[i].x,a[i].y)>i?"Y":"N");
}
return ;
}

或者可以线段树分治。

线段树分治就是离线,按操作时间建一个线段树,把修改放在树上,然后遍历线段树,支持修改的栈序撤销,走到叶子就可以知道那个时刻的答案。

这道题就是给线段树节点开 vector 存它有些什么边,然后并查集按秩合并,就可以栈序撤销了。

比 LCT 快了一倍。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#define ls Ls[cr]
#define rs Rs[cr]
#define pb push_back
#define mkp make_pair
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=5e5+,K=;
int n,m,op[M],fa[N],siz[N],top; bool vis[M],ans[M];
int tot,Ls[M<<],Rs[M<<],sm[M<<];
struct Node{
int x,y;
Node(int x=,int y=):x(x),y(y) {}
}a[M],sta[N];
vector<Node> vt[M<<];
map<pair<int,int>,int> mp;
void build(int l,int r,int cr)
{
if(l==r)return; int mid=l+r>>;
ls=++tot; build(l,mid,ls);
rs=++tot; build(mid+,r,rs);
}
void ins(int l,int r,int cr,int L,int R,Node k)
{
if(l>=L&&r<=R){vt[cr].pb(k);return;}
int mid=l+r>>;
if(L<=mid)ins(l,mid,ls,L,R,k);
if(mid<R)ins(mid+,r,rs,L,R,k);
}
void cz(int l,int r,int cr)
{
if(l==r){sm[cr]=(op[l]==?:);return;}
int mid=l+r>>;
cz(l,mid,ls); cz(mid+,r,rs);
sm[cr]=sm[ls]+sm[rs];
}
int fnd(int a){return fa[a]==a?a:fnd(fa[a]);}
void mrg(Node a)
{
int x=fnd(a.x),y=fnd(a.y);
if(x==y)return; if(siz[x]<siz[y])swap(x,y);
sta[++top]=Node(y,x); fa[y]=x; siz[x]+=siz[y];
}
void solve(int l,int r,int cr)
{
int sz=vt[cr].size();
for(int i=;i<sz;i++)mrg(vt[cr][i]);
if(l==r)
{ans[l]=(fnd(a[l].x)==fnd(a[l].y));return;}
int mid=l+r>>;
if(sm[ls])
{
int nw=top; solve(l,mid,ls);
for(int& i=top;i>nw;i--)
{
int x=sta[i].x,y=sta[i].y;
fa[x]=x; siz[y]-=siz[x];
}
}
if(sm[rs])
{
int nw=top; solve(mid+,r,rs);
for(int& i=top;i>nw;i--)
{
int x=sta[i].x,y=sta[i].y;
fa[x]=x; siz[y]-=siz[x];
}
}
}
int main()
{
n=rdn();m=rdn();
tot=;build(,m,);
for(int i=;i<=m;i++)
{
op[i]=rdn();int x=rdn(),y=rdn();
if(x>y)swap(x,y);//
if(!op[i])
{
a[i]=Node(x,y);
mp[mkp(x,y)]=i;
}
else if(op[i]==)
{
int bh=mp[mkp(x,y)]; vis[bh]=;
ins(,m,,bh,i-,a[bh]);
}
else a[i]=Node(x,y);
}
for(int i=;i<=m;i++)
if(!op[i]&&!vis[i])ins(,m,,i,m,a[i]);
cz(,m,); for(int i=;i<=n;i++)fa[i]=i,siz[i]=;
solve(,m,);
for(int i=;i<=m;i++)
if(op[i]==)puts(ans[i]?"Y":"N");
return ;
}

LOJ 121 「离线可过」动态图连通性——LCT维护删除时间最大生成树 / 线段树分治的更多相关文章

  1. LOJ #121. 「离线可过」动态图连通性 LCT维护最大生成树

    这个还是比较好理解的. 你考虑如果所有边构成一棵树的话直接用 LCT 模拟一波操作就行. 但是可能会出现环,于是我们就将插入/删除操作按照时间排序,然后依次进行. 那么,我们就要对我们维护的生成树改变 ...

  2. LOJ#121. 「离线可过」动态图连通性(线段树分治)

    题意 板子题,题意很清楚吧.. Sol 很显然可以直接上LCT.. 但是这题允许离线,于是就有了一个非常巧妙的离线的做法,好像叫什么线段树分治?? 此题中每条边出现的位置都可以看做是一段区间. 我们用 ...

  3. loj#121.「离线可过」动态图连通性

    题面 话说#122怎么做啊 题解 我的\(\mathrm{LCT}\)水平极差,连最小生成树都快忘了,赶紧复习一下 做法和这篇是一样的 这道题还可以练习线段树分治 还可以练习ETT 果然是道吼题 代码 ...

  4. 【LOJ】#121. 「离线可过」动态图连通性

    题解 和BZOJ4025挺像的 就是维护边权是时间的最大生成树 删边直接删 两点未联通时直接相连,两点联通则找两点间边权小的一条边删除即可 代码 #include <bits/stdc++.h& ...

  5. 【LOJ121】「离线可过」动态图连通性

    [LOJ121]「离线可过」动态图连通性 题面 LOJ 题解 线段树分治的经典应用 可以发现每个边出现的时间是一个区间 而我们每个询问是一个点 所以我们将所有边的区间打到一颗线段树上面去 询问每个叶子 ...

  6. LOJ121 「离线可过」动态图连通性

    思路 动态图连通性的板子,可惜我不会在线算法 离线可以使用线段树分治,每个边按照存在的时间插入线段树的对应节点中,最后再dfs一下求出解即可,注意并查集按秩合并可以支持撤销操作 由于大量使用STL跑的 ...

  7. 「LOJ 121」「离线可过」动态图连通性「按时间分治 」「并查集」

    题意 你要维护一张\(n\)个点的无向简单图.你被要求执行\(m\)条操作,加入删除一条边及查询两个点是否连通. 0:加入一条边.保证它不存在. 1:删除一条边.保证它存在. 2:查询两个点是否联通. ...

  8. LOJ 546: 「LibreOJ β Round #7」网格图

    题目传送门:LOJ #546. 题意简述: 题目说的很清楚了. 题解: 将不包含起点或障碍物的连续的行或列缩成一行或一列,不会影响答案. 处理过后,新的网格图的行数和列数最多为 \(2k + 3\). ...

  9. LOJ121 【离线可过】动态图连通性

    题目链接:戳我 [线段树分治版本代码] 这里面的线段树是时间线段树,每一个节点都要开一个vector,记录当前时间区间中存在的边的标号qwq #include<iostream> #inc ...

随机推荐

  1. JQuery $未定义

    ---恢复内容开始--- JQuery $未定义 转载▼   jquery是Yii集成的,利用jquery写的代码$(document).ready(function(){// 操作列表$('.ope ...

  2. spring 监听器 IntrospectorCleanupListener

    org.springframework.web.util.IntrospectorCleanupListener监听器 主要负责处理由JavaBean Introspector使用而引起的缓冲泄露,  ...

  3. 桥接、nat、host-only

  4. Awk 从入门到放弃 (6) Awk模式(Pattern)之二

    第一题:从/etc/passwd 匹配以 vmuser开头的用户 grep “^vmuser”   /etc/passwd  -r  -n awk  ‘/^vmuser /{print $0}’  / ...

  5. python 加密 解密 签名 验证签名 公钥 私钥 非对称加密 RSA

    加密与解密,这很重要,我们已经可以用pypcap+npcap简单嗅探到网络中的数据包了.而这个工具其实可以嗅探到更多的数据.如果我们和别人之间传输的数据被别人嗅探到,那么信息泄漏,信息被篡改,将给我们 ...

  6. CSRmesh Application

    1.CSRmesh Control Application v2.0.1版本可以运行在支持BLE4.0的Android设备上,该版本在Android4.4.4.5.0.5.0.1.5.0.2.5.1和 ...

  7. 根据ip,实现地址信息查询接口

    偶然发现的360搜索的ip查询接口,记录下: 接口地址:https://m.so.com/position 使用方式1:传ip 如访问https://m.so.com/position?ip=47.1 ...

  8. L1-025 正整数A+B

    题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000].稍微有点麻烦的是,输入并不保证是两个正整数. 输入格式: 输入在一行给出A和B,其间以空格分开.问题是A和B不一定是满足 ...

  9. 玩转X-CTR100 | STM32F4 l GPIO位带操作

    更多塔克创新资讯欢迎登陆[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]       STM32F4位带概念,及位带的GPIO操作实践应用. 原理介 ...

  10. WIN-8“内置管理员无法激活此应用”问题

    解决办法:在运行中输入:“gpedit.msc”,就会启动组策略编辑器,依次展开“计算机配置”里面的“Windows设置”,然后是“安全设置”,再就是“本地策略”里面的“安全选项”,在右边查找一项策略 ...