【POI 2007】Office 办公楼(BIU)
http://www.zybbs.org/JudgeOnline/problem.php?id=1098
题目大意:给定一个N个点M条边的无向图,将N个点分成尽量多的组,满足任意两个不在同一组的点之间都有边。
此题的答案就是原图补图的联通块个数。具体在WC2011 MT的课件中有。
原话是:将未访问点挂链,扩展一个点时,标记原图中该点的相邻点,遍历链表,将所有未标记点(即该点在补图中的邻居)入队并从链表中删除。
然后我就根据这个很笨拙地串了一条链……
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <vector> #include <algorithm> #define mm 2000100 #define mn 100003 using namespace std; queue<int> q; vector<int> v; int n,m,a,b,ans; bool vis[mn]; struct EDGE{ int pnt; EDGE *pre; EDGE(){} EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){} }Edge[mm*2],*SP=Edge,*edge[mm]; struct LINE{ int pre,next; bool flag; }line[mn]; inline void addedge(int a,int b){ edge[a]=new(++SP)EDGE(b,edge[a]); edge[b]=new(++SP)EDGE(a,edge[b]); } void Del(int i){ line[line[i].next].pre=line[i].pre; line[line[i].pre].next=line[i].next; } void bfs(){ while(line[0].next!=-1){ q.push(line[0].next); vis[line[0].next]=true; Del(line[0].next); int cnt=1; while(!q.empty()){ int i=q.front();q.pop(); for(EDGE *j=edge[i];j;j=j->pre) line[j->pnt].flag=true; for(int j=line[0].next;j>0;j=line[j].next) if(!line[j].flag && !vis[j]){ q.push(j); vis[j]=true; Del(j); cnt++; } for(int j=line[0].next;j>0;j=line[j].next) line[j].flag=false; } ans++; v.push_back(cnt); } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); addedge(a,b); } for(int i=1;i<=n;i++) line[i-1].next=i,line[i].pre=i-1; line[n].next=-1; bfs(); sort(v.begin(),v.end()); printf("%d\n",ans); if(!v.empty()){ for(int i=0;i<v.size()-1;i++) printf("%d ",v[i]); printf("%d\n",v[v.size()-1]); } return 0; }