/*
poj2942 双连通分量
Author: lcy
Time: 2017-11-8

白书例题
跑一遍双联通分量。对于每个双联通分量,
只要其中存在一个奇环,对于任意一个骑士,
我们便可以构造一个包含他的奇环。
*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
#define ll long long
#define fr(i,a,b) for(int i=a;i<=b;i++)
#define frr(i,a,b) for(int i=a;i>=b;i--)
#define ms(a,b) memset(a,b,sizeof(a))
#define scfd(a) scanf("%d",a)
#define scflf(a) scanf("%lf",a)
#define scfs(a) scanf("%s",a)
#define ptfd(a) printf("%d\n",a)
#define ptfs(a) printf("%s\n",a)
#define showd(a,b) printf(a"=%d\n",b)
#define showlf(a,b) printf(a"=%lf\n",b)
#define shows(a,b) printf(a"=%s\n",b)
#define mmcp(a,b) memcpy(a,b,sizeof(b))
#define pb(a) push_back(a)
const int MAXN=1005;
struct edge{
	int f,t;
};
vector<edge>e;
vector<int>g[MAXN];
vector<int>bcc[MAXN];
stack<edge>s;//栈存边
//如果存点,由于dfs每个点只入栈一次,而割顶属于可能属于多个bcc,弹栈后使得后面的bcc少了该点。
//由于两个bcc至多有一个公共点且该点为割顶,所以没有公共边。
//dfs时,每个边入栈一次,而每个边又至多属于一个bcc。故存边更加方便。
int tot,p[MAXN],bidx[MAXN],bcnt;//bcccnt从1开始,0代表没有进入任何bcc
bool iscut[MAXN];//同时求出割顶
inline void add_edge(int f,int t){
	//printf("build %d to %d\n",f,t);
	e.push_back((edge){f,t});
	e.push_back((edge){t,f});
	g[f].push_back(e.size()-2);
	g[t].push_back(e.size()-1);
}
int dfs(int x,int fa){//返回最low
	//printf("now move to %d\n",x);
	int lowx,sz,cnt=0;
	lowx=p[x]=++tot;//从1开始,0代表没有访问	
	sz=g[x].size();
	fr(i,0,sz-1){
		edge ne=e[g[x][i]];	
		if(!p[ne.t]){
			s.push(ne);	
			int lowt=dfs(ne.t,x);
			lowx=min(lowx,lowt);
			if(lowt>=p[x]){//x是割顶,将子树及x加入同一双联通分量
				iscut[x]=true;
				cnt++;
				bcnt++;bcc[bcnt].clear();//多组数据,随用随清
				//puts("Another bcc");
				for(;;){
					edge t=s.top();s.pop();
					if(bidx[t.f]!=bcnt){//防止子树中有的点进入bcc多次,同时处理割顶
						//printf("%d ",t.f);
						bidx[t.f]=bcnt;
						bcc[bcnt].push_back(t.f);	
					}	
					if(bidx[t.t]!=bcnt){
						//printf("%d ",t.t);
						bidx[t.t]=bcnt;
						bcc[bcnt].push_back(t.t);	
					}	
					if(t.f==x&&t.t==ne.t)break;
				}	
				//puts("\nbcc");
			}
		}	
		else if(p[ne.t]<p[x]&&ne.t!=fa){
			s.push(ne);
			lowx=min(lowx,p[ne.t]);//用从x出发反向边更新
		}
	}
	if(cnt==1&&fa==-1)iscut[x]=false;//树根至少需要两个子树才能成为割顶
	return lowx;
}
int n,m,col[MAXN];
bool hate[MAXN][MAXN],ok[MAXN];
inline void init(){
	tot=bcnt=0;
	e.clear();
	fr(i,0,MAXN-1)g[i].clear();
	while(!s.empty())s.pop();
	ms(p,0);
	ms(iscut,false);
	ms(bidx,0);
	ms(hate,false);
	ms(ok,false);
	ms(col,0);
}
inline void find_bcc(){
	fr(i,1,n)if(!p[i])dfs(i,-1);	
}
bool chk(int x,int b){
	int sz=g[x].size();
	int nc=((col[x]-1)^1)+1;
	//printf("x=%d,nc=%d\n",x,nc);
	fr(i,0,sz-1){
		edge ne=e[g[x][i]];
		if(bidx[ne.t]!=b)continue;
		if(!col[ne.t]){
			col[ne.t]=nc;
			bool t=chk(ne.t,b);	
			if(!t)return t;
		}	
		else if(col[ne.t]!=nc)
			return false;		
	}	
	return true;
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		if(!n&&!m)break;
		init();
		fr(i,1,m){
			int a,b;
			scanf("%d%d",&a,&b);
			hate[a][b]=hate[b][a]=true;	
		}	
		fr(i,1,n)
			fr(j,i+1,n)
				if(!hate[i][j])
					add_edge(i,j);
		find_bcc();
		int ans=0;	
		fr(i,1,bcnt){
			int sz=bcc[i].size();
			fr(j,0,sz-1)bidx[bcc[i][j]]=i;//把割顶标回来
			col[bcc[i][0]]=1;
			if(!chk(bcc[i][0],i))//存在奇圈
				fr(j,0,sz-1)ok[bcc[i][j]]=true;
			fr(j,0,sz-1)col[bcc[i][j]]=0;//还原颜色标记,割顶
		}	
		fr(i,1,n)if(ok[i])ans++;
		ans=n-ans;
		ptfd(ans);
	}	
	return 0;
}













 posted on 2017-11-08 22:33  cylcy  阅读(85)  评论(0编辑  收藏  举报