P5227 [AHOI2013] 连通图

P5227 [AHOI2013] 连通图

题目描述

给定一个无向连通图和若干个小集合,每个小集合包含一些边,对于每个集合,你需要确定将集合中的边删掉后改图是否保持联通。集合间的询问相互独立

定义一个图为联通的当且仅当对于任意的两个顶点,都存在一条路径连接它们

数据范围

1  n,k  105

1  m  2 × 105

1  c  4

保证不存在重边和自环。

Solution:

好神的线段树分治.

首先我们将询问想象成时间,那么我们对每条边都可以维护它在那些时间是在线的,那些时间是离线的。我们按时间维护一颗线段树,然后我们再将这些边挂到线段树上。在进入一个线段树区间 [l,r] 时,表示当前在统计的答案的时间戳落在了这个区间内,那么我们就要将在当前时间区间 [l,r] 在线的边全部加进来,退出这个节点的时候将即将要离线的边删除。但是我们发现删除并不好维护,所以我们考虑用可撤销并查集维护联通性

实现:

我们将询问想象成一个标记永久化的东西,这样我们能使得时间复杂度很优秀,在退出一个点时,将这个点下的标记全部从并查集中撤销,用栈来存储需要删除的东西。

至于可撤销并查集:

就是在合并两个集合的时候先记一下他们原来的状态 dep,siz,fa 要删除时将两个点的状态还原回来。

Code:

#include<bits/stdc++.h>
const int N=2e5+5;
using namespace std;
int n,m,q,cnt;
int ans[N],a[N],b[N];
int siz[N],dep[N],fa[N];
vector<int> tim[N];
int find(int x){return fa[x]==x ? x : find(fa[x]);}
struct Merge{
int fx,fy,dep_fx,dep_fy,siz_fx,siz_fy;
}st[N<<2];
struct Segment_Tree{
struct Tree{
int l,r;
vector<int> V;
}t[N<<2];
#define ls x<<1
#define rs x<<1|1
#define mid (t[x].l+t[x].r>>1)
void build(int x,int l,int r)
{
t[x].l=l,t[x].r=r;t[x].V.clear();
if(l==r)return;
build(ls,l,mid);build(rs,mid+1,r);
}
void upd(int x,int L,int R,int id)
{
if(L<=t[x].l&&t[x].r<=R){t[x].V.emplace_back(id);return;}
if(L<=mid)upd(ls,L,R,id);
if(mid<R) upd(rs,L,R,id);
}
void dfs(int x)
{
int tmp=cnt;
for(auto id : t[x].V)
{
int u=find(a[id]),v=find(b[id]);
if(u!=v)
{
if(dep[u]>dep[v])swap(u,v);
st[++cnt]={u,v,dep[u],dep[v],siz[u],siz[v]};
dep[v]+=(dep[v]==dep[u]);siz[v]+=siz[u];fa[u]=v;
}
}
if(t[x].l==t[x].r){ans[t[x].l]=(siz[find(1)]==n ? 1 : 0);}
else {dfs(ls);dfs(rs);}
while(cnt>tmp)
{
auto [x,y,dep_x,dep_y,siz_x,siz_y] = st[cnt];cnt--;
dep[x]=dep_x,dep[y]=dep_y;
siz[x]=siz_x,siz[y]=siz_y;
fa[x]=x,fa[y]=y;
}
}
}T;
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=dep[i]=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
}
cin>>q;
T.build(1,1,q);
for(int i=1,c;i<=q;i++)
{
scanf("%d",&c);
for(int j=1,x;j<=c;j++)
{
scanf("%d",&x);
tim[x].push_back(i);
}
}
for(int u=1,last;u<=m;u++)
{
last=0;tim[u].push_back(q+1);
for(auto now : tim[u])
{
if(last+1<=now-1)T.upd(1,last+1,now-1,u);last=now;
}
}
T.dfs(1);
for(int i=1;i<=q;i++)
{
printf(ans[i] ? "Connected\n" : "Disconnected\n");
}
}
int main()
{
//freopen("P5227.in","r",stdin);freopen("P5227.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示