点双杀我。
一年多没写点双竟幼稚的以为栈里存的是点,awsl。
具体题解参考刘汝佳的蓝书(滑稽),这里提醒几个细节:
1.这样多组数据的,在时间允许的情况下尽量要把所有数组和vector什么的都清空,除非你特别特别特别确定哪些不用清空。
2.割点是在>=2个点双中的,所以 每次判断一个点双是不是二分图时,一定要先把所有点双内的点标记一下tag,否则割点只会指向其中一个点双。
3.点双栈里存的是边啦,不是点。。。。 顺便请注意一下,求点双,边双,scc的三种tarjan算法的区别。
#include<cstdio> #include<vector> #include<stack> #include<cctype> #define ll long long using namespace std; #define pb push_back const int N=1005; struct edge{ int x,y;}; stack<edge> s; vector<int> g[N],lt[N]; int n,m,k,dc,dfn[N],low[N]; int v[N],col[N],now; bool cc[N][N],a[N]; inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x; } inline void init(){ fill(a+1,a+n+1,0); fill(dfn+1,dfn+n+1,0),dc=0; for(int i=1;i<=k;i++) lt[i].clear(); for(int i=1;i<=n;i++){ g[i].clear(); fill(cc[i]+1,cc[i]+n+1,0); } while(!s.empty()) s.pop(); } void dfs(int x,int fa){ dfn[x]=low[x]=++dc; for(int i:g[x]) if(i!=fa) if(!dfn[i]){ s.push((edge){x,i}),dfs(i,x),low[x]=min(low[x],low[i]); if(low[i]>=dfn[x]){ k++; for(edge e;;){ e=s.top(),s.pop(); if(v[e.x]!=k) v[e.x]=k,lt[k].pb(e.x); if(v[e.y]!=k) v[e.y]=k,lt[k].pb(e.y); if(e.x==x&&e.y==i) break; } } } else low[x]=min(low[x],dfn[i]); } bool bc(int x,int cl){ col[x]=cl; for(int i:g[x]) if(v[i]==now) if(col[i]==cl) return 0; else if(!col[i]) if(!bc(i,cl^3)) return 0; return 1; } inline void solve(){ for(int U,V;m;m--) U=read(),V=read(),cc[U][V]=cc[V][U]=1; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(!cc[i][j]) g[i].pb(j),g[j].pb(i); for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,0); for(now=1;now<=k;now++){ for(int i:lt[now]) col[i]=0,v[i]=now; if(!bc(lt[now][0],1)) for(int i:lt[now]) a[i]=1; } int ans=0; for(int i=1;i<=n;i++) ans+=!a[i]; printf("%d ",ans); } int main(){ while(scanf("%d%d",&n,&m)==2&&n&&m){ init(); solve(); } return 0; }