白书上有详细解说
代码与书上有些不同,但核心是相通的
//LA 3523 点双连通+二分判奇圈 #include <cstdio> #include <algorithm> #include <iostream> #include <string.h> #include <vector> #include<stack> #include <stack> #define inf 0x3f3f3f3f #define lowbit(x) ((x)&(-x)) #define maxn 1050 using namespace std; int dfn[maxn],low[maxn],n,m,head[maxn],cnt,cntt,belong[maxn],ans,block,color[maxn],e[maxn][maxn],iscut[maxn],in[maxn]; vector<int> blo[maxn]; struct ee { int next,to,fa; }eage[maxn*maxn*2]; stack<ee> s; void add(int a,int b) { eage[cntt].to=b; //eage[cntt].w=c; eage[cntt].next=head[a]; eage[cntt].fa=a; head[a]=cntt++; } void init() { memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(belong,0,sizeof(belong)); memset(iscut,0,sizeof(iscut)); memset(in,0,sizeof(in)); memset(e,0,sizeof(e)); cnt=cntt=0; ans=0; block=0; while(!s.empty()) s.pop(); for(int i=0;i<=n;i++) blo[i].clear(); } void tarjian(int a,int b) { dfn[a]=low[a]=++cnt; int child=0; for(int i=head[a];i!=-1;i=eage[i].next) { int j=eage[i].to; if(j==b) continue; if(!dfn[j]) { s.push(eage[i]); child++; tarjian(j,a); low[a]=min(low[j],low[a]); if(low[j]>=dfn[a]) { block++; iscut[a]=1; while(1) { ee x=s.top(); s.pop(); if(belong[x.fa]!=block) { belong[x.fa]=block; blo[block].push_back(x.fa); } if(belong[x.to]!=block) { belong[x.to]=block; blo[block].push_back(x.to); } if(x.fa==a&&x.to==j) break; } } } else if(dfn[j]<dfn[a]) //这个很关键如果不这样的话,s中会加两次回边。 {s.push(eage[i]);low[a]=min(low[a],dfn[j]);} } if(b<0&&child==1) iscut[a]=0; } bool twojudge(int a,int b) { for(int i=head[a];i!=-1;i=eage[i].next) { int j=eage[i].to; if(belong[j]==b) { if(color[j]==color[a]) return false; if(!color[j]) { color[j]=3-color[a]; if(!twojudge(j,b)) return false; } } } return true; } int main () { //freopen("d:\\in.txt","r",stdin); while(~scanf("%d%d",&n,&m)&&(n+m)) { init(); if(n==0&&m==0) break; for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); e[a][b]=1; e[b][a]=1; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(!e[i][j]&&j!=i) { add(i,j); } for(int i=1;i<=n;i++) { if(!dfn[i]) { tarjian(i,-1); } } for(int i=1;i<=block;i++) { memset(color,0,sizeof(color)); //由于割点会重复,所以要清空。 for(int j=0;j<blo[i].size();j++) belong[blo[i][j]]=i; int u=blo[i][0]; color[u]=1; if(!twojudge(u,i)) { for(int j=0;j<blo[i].size();j++) in[blo[i][j]]=1; } } ans=n; for(int i=1;i<=n;i++) if(in[i]) ans--; printf("%d\n",ans); } return 0; }