C134 线段树分治+并查集 P5227 [AHOI2013] 连通图
视频链接:C134 线段树分治 P5227 [AHOI2013] 连通图_哔哩哔哩_bilibili
P5227 [AHOI2013] 连通图 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
// 线段树分治 O(nlognlogn) #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define int long long #define ls (u<<1) #define rs (u<<1|1) #define mid (l+r>>1) const int N=100005; int n,m,k; int p[N],high[N],siz[N],top; pair<int,int>e[N<<1]; //存边 vector<int>del[N<<1]; //边i消失的时刻 vector<int>tr[N<<2]; //线段树节点 struct node{ int x,y,hy; }st[N<<1]; //用栈记录边的端点的合并信息 void insert(int u,int l,int r,int L,int R,int i){ if(L<=l&&r<=R) return tr[u].push_back(i); if(L<=mid) insert(ls,l,mid,L,R,i); if(R>mid) insert(rs,mid+1,r,L,R,i); } int find(int x){ //查找根 return p[x]==x?x:find(p[x]); } void merge(int x,int y){ //合并集合 x=find(x),y=find(y); if(x==y) return; if(high[x]>high[y]) swap(x,y); st[++top]={x,y,high[y]}; p[x]=y; //x连向y high[y]+=(high[y]==high[x]); siz[y]+=siz[x]; //累加连通块的大小 } void solve(int u,int l,int r){ int now=top; //合并当前区间出现的边 for(auto i:tr[u]) merge(e[i].first,e[i].second); //判断去掉r时刻集合中的边后图是否联通 if(l==r) siz[find(1)]==n? puts("Connected"):puts("Disconnected"); else solve(ls,l,mid),solve(rs,mid+1,r); while(top>now){ //撤销合并 node s=st[top--]; p[s.x]=s.x; high[s.y]=s.hy; siz[s.y]-=siz[s.x]; } } signed main(){ scanf("%lld%lld",&n,&m); for(int i=1;i<=n;++i) p[i]=i,siz[i]=1; for(int i=1,x,y;i<=m;++i){ scanf("%lld%lld",&x,&y); e[i].first=x,e[i].second=y; del[i].push_back(0); } scanf("%lld",&k); for(int i=1,c,j;i<=k;++i){ //k个时刻 scanf("%lld",&c); //删c条边 while(c--){ scanf("%lld",&j); del[j].push_back(i); //边j消失的时刻 } } for(int i=1;i<=m;++i){ del[i].push_back(k+1); for(int j=0;j<del[i].size()-1;++j) if(del[i][j+1]-del[i][j]>1) insert(1,1,k,del[i][j]+1,del[i][j+1]-1,i); //边i出现的时间[del[i][j]+1,del[i][j+1]-1] } solve(1,1,k); }
// 线段树分治 O(nlognlogn) #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define int long long #define ls (u<<1) #define rs (u<<1|1) #define mid (l+r>>1) const int N=100005; int n,m,k; int p[N],siz[N],top; pair<int,int>e[N<<1]; //存边 vector<int>del[N<<1]; //边i消失的时刻 vector<int>tr[N<<2]; //线段树节点 struct node{ int x,y; }st[N<<1]; //用栈记录边的端点的合并信息 void insert(int u,int l,int r,int L,int R,int i){ if(L<=l&&r<=R) return tr[u].push_back(i); if(L<=mid) insert(ls,l,mid,L,R,i); if(R>mid) insert(rs,mid+1,r,L,R,i); } int find(int x){ //查找根 return p[x]==x?x:find(p[x]); } void merge(int x,int y){ //合并集合 x=find(x),y=find(y); if(x==y) return; if(siz[x]>siz[y]) swap(x,y); st[++top]={x,y}; p[x]=y; siz[y]+=siz[x]; } void solve(int u,int l,int r){ int now=top; for(auto i:tr[u]) merge(e[i].first,e[i].second); if(l==r) siz[find(1)]==n? puts("Connected"):puts("Disconnected"); else solve(ls,l,mid),solve(rs,mid+1,r); while(top>now){ //撤销合并 node s=st[top--]; p[s.x]=s.x; siz[s.y]-=siz[s.x]; } } signed main(){ scanf("%lld%lld",&n,&m); for(int i=1;i<=n;++i) p[i]=i,siz[i]=1; for(int i=1,x,y;i<=m;++i){ scanf("%lld%lld",&x,&y); e[i].first=x,e[i].second=y; del[i].push_back(0); } scanf("%lld",&k); for(int i=1,c,j;i<=k;++i){ //k个时刻 scanf("%lld",&c); //删c条边 while(c--){ scanf("%lld",&j); del[j].push_back(i); //边j消失的时刻 } } for(int i=1;i<=m;++i){ del[i].push_back(k+1); for(int j=0;j<del[i].size()-1;++j) if(del[i][j+1]-del[i][j]>1) insert(1,1,k,del[i][j]+1,del[i][j+1]-1,i); //边i出现的时间[del[i][j]+1,del[i][j+1]-1] } solve(1,1,k); }
分类:
C 数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!