点双杀我。
一年多没写点双竟幼稚的以为栈里存的是点,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;
}