题解 P3452 【[POI2007]BIU-Offices】
这是一个求原图的补图联通块个数的题
我们可以知道如果一个点在原图中不能到达另一个点
那么这个点一定可以在补图中到达那个点
但是原图可以到达的点补图不一定不能到达
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
bool vis[100010],cov[100010];
int cnt,head[100010],nxt[4000100],var[4000100];
int nex[100010],last[100010];
int st[100010],ans;
int n,m;
queue<int> q;
void add(int x,int y) {
cnt++;
var[cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}//链式向前星,蒟蒻不会vector QWQ
void del(int x) {
nex[last[x]]=nex[x];
last[nex[x]]=last[x];
}//删除链表中的某个元素
int main() {
scanf("%d%d",&n,&m);
int x,y;
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);//建图
}
nex[0]=1;
for(int i=1; i<n; i++) {
last[i+1]=i;
nex[i]=i+1;
}
//可以理解为链表,访问的时候可以快一点
for(int i=1; i<=n; i++) {
if(!vis[i]) {
//如果没被访问过
st[++ans]=1;//新开一个联通块
vis[i]=true;//标记
q.push(i);//入队
del(i);//删除这个点
while(!q.empty()) {
int x=q.front();
q.pop();
for(int j=head[x]; j; j=nxt[j]) {
if(!vis[var[j]]) {
//如果原图可以到达的点没有访问过
cov[var[j]]=true;
//标记,原图可以访问
}
}
for(int j=nex[0]; j; j=nex[j]) {
if(!cov[j]) {
//访问不到的全都入队,因为在补图里可以访问到
vis[j]=true;
//这个点在补图里访问过了
st[ans]++;
//联通块里的点的个数++;
del(j);
//删除这个点
q.push(j);
//入队
} else cov[j]=false;
//如果在原图中可以访问到,那就没他什么事了,恢复原样吧
}
}
}
}
sort(st+1,st+ans+1);//排序一下
printf("%d\n",ans);
for(int i=1; i<=ans; i++) {
printf("%d ",st[i]);
}
return 0;
//return 0不知道有没有必要,这几天Claris上课标程永远没有return 0,但是蒟蒻又不敢不打,瑟瑟发抖
}
感谢 奔波儿霸 指出错误