POJ2942:Knights of the Round Table——题解
http://poj.org/problem?id=2942
所写的tarjan练习题最难的一道。
说白了难在考得不是纯tarjan。
首先我们把仇恨关系处理成非仇恨关系的图,然后找双连通分量,在双连通分量里的点满足了任意一个人可以和两个(或以上)的人坐一起。
那么我们接下来要判断奇环。
发现性质:如果一个双连通分量有奇环,那么其中任意一点一定在某个奇环上。
也就是说,这些人拼一拼绝对能全部开会成功,我们把他们打上成功标志。
然后搜失败标志的人的个数即可。
判断奇环的方法显然二分图染色。
#include<stack> #include<cstdio> #include<cstring> #include<vector> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; inline int read(){ int x=0,w=1;char ch=0; while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*w; } const int maxn=1001; struct node{ int st; int to; int nxt; }edge[2000001]; int cnt,head[maxn]; void add(int u,int v){ cnt++; edge[cnt].st=u; edge[cnt].to=v; edge[cnt].nxt=head[u]; head[u]=cnt; return; } bool dis[maxn][maxn]; bool ok[maxn]; int color[maxn]; int dfn[maxn]; int low[maxn]; bool inslt[maxn]; int t=0; int n,m; int numslt[maxn]; stack<int>q; vector<int>slt[maxn]; int slt_cnt; void tarjan(int u,int f){ t++; dfn[u]=t; low[u]=t; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(!dfn[v]){ q.push(i); tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ slt_cnt++; slt[slt_cnt].clear(); while(233){ int num=q.top(); q.pop(); if(numslt[edge[num].st]!=slt_cnt){ numslt[edge[num].st]=slt_cnt; slt[slt_cnt].push_back(edge[num].st); } if(numslt[edge[num].to]!=slt_cnt){ numslt[edge[num].to]=slt_cnt; slt[slt_cnt].push_back(edge[num].to); } if(edge[num].to==v&&edge[num].st==u)break; } } }else if(f!=v){ if(low[u]>dfn[v]){ q.push(i); low[u]=dfn[v]; } } } return; } bool draw(int u){ bool ret=0; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(!inslt[v])continue; if(color[v]==-1){ color[v]=1-color[u]; ret|=draw(v); }else if(color[v]==color[u]){ return 1; } } return ret; } void clr(){ cnt=0;slt_cnt=0; while(!q.empty())q.pop(); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(numslt,0,sizeof(numslt)); memset(head,0,sizeof(head)); memset(dis,0,sizeof(dis)); memset(ok,0,sizeof(ok)); return; } int main(){ n=read(); m=read(); while(n||m){ clr(); for(int i=1;i<=m;i++){ int u=read(); int v=read(); dis[u][v]=dis[v][u]=1; } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(!dis[i][j]){ add(i,j); add(j,i); } } } for(int i=1;i<=n;i++){ if(!dfn[i]){ tarjan(i,0); } } for(int i=1;i<=slt_cnt;i++){ memset(inslt,0,sizeof(inslt)); memset(color,-1,sizeof(color)); int u; for(int j=0;j<slt[i].size();j++){ u=slt[i][j]; inslt[u]=1; } color[u]=0; if(draw(u)){ for(int j=0;j<slt[i].size();j++){ u=slt[i][j]; ok[u]=1; } } } int ans=0; for(int i=1;i<=n;i++)if(!ok[i])ans++; printf("%d\n",ans); n=read();m=read(); } return 0; }