此后如竟没有炬火 我便是唯一的光|

Day_Dreamer_D

园龄:3年6个月粉丝:3关注:16

2022-09-14 21:45阅读: 326评论: 0推荐: 0

洛谷 P3452 [POI2007]BIU-Offices 题解

简化题意:求原图的补图的连通块个数。

众所周知,补图有这样一条性质:若图不连通,则其补图一定连通。证明略。

于是我们考虑枚举满足 visu=0 的点 u,然后将其 push 进队列并打上标记(vis)。然后枚举与 u 在补图上相邻的且 visv=0 的点 v,再将其加入队列并打标记······如此反复直至队列为空为止。

我们考虑优化,结合链表的思想,我们可以维护出每个点之后第一个未被标记的点,然后向后跳即可找到所有 v。如此我们还需维护删除操作,所以用链表来实现。

时间复杂度 O(n+m)

//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 中国大陆许可协议进行许可。

posted @   Day_Dreamer_D  阅读(326)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起