洛谷 P3452 [POI2007]BIU-Offices 题解
简化题意:求原图的补图的连通块个数。
众所周知,补图有这样一条性质:若图不连通,则其补图一定连通。证明略。
于是我们考虑枚举满足 的点 ,然后将其 push 进队列并打上标记(vis)。然后枚举与 在补图上相邻的且 的点 ,再将其加入队列并打标记······如此反复直至队列为空为止。
我们考虑优化,结合链表的思想,我们可以维护出每个点之后第一个未被标记的点,然后向后跳即可找到所有 。如此我们还需维护删除操作,所以用链表来实现。
时间复杂度 。
//P3452 [POI2007]BIU-Offices #pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int MAXN=100010; /***********图论***********/ struct Edge { int to,nxt; }e[4000100]; int cnt,head[MAXN]; bool vis[MAXN],exi[MAXN]; queue<int> q; void addedge(int x,int y) { e[++cnt].nxt=head[x]; head[x]=cnt; e[cnt].to=y; return; } /***********链表***********/ int l[MAXN]/*pre*/,r[MAXN]/*nxt*/; void del(int x) { r[l[x]]=r[x]; l[r[x]]=l[x]; return; } /***********Main***********/ int n,m,ans[MAXN],sum; int main() { int x,y,u,v; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } r[0]=1; for(int i=1;i<n;i++) { l[i+1]=i; r[i]=i+1; } for(int i=1;i<=n;i++) { if(!vis[i]) { ans[++sum]=1;//新开连通块 q.push(i); vis[i]=true; del(i); while(!q.empty()) { u=q.front(); q.pop(); for(int j=head[u];j;j=e[j].nxt) { v=e[j].to; if(!vis[v]) { exi[v]=true;//如果原图可以到达的点没有访问过,则标记为可以访问 } } for(int j=r[0];j;j=r[j]) { if(!exi[j])//访问不到的全都入队,因为在补图里可以访问到 { vis[j]=true; ans[sum]++; del(j); q.push(j); } else { exi[j]=false; } } } } } sort(ans+1,ans+sum+1); printf("%d\n",sum); for(int i=1;i<=sum;i++) { printf("%d ",ans[i]); } return 0; } /* * 洛谷 * https://www.luogu.com.cn/problem/P3452 * C++20 -O2 * 2022.9.14 */
本文作者:Day_Dreamer_D
本文链接:https://www.cnblogs.com/2020gyk080/p/16694693.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
标签:
,
,
,
,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步