题目
n(n<=1e3)个骑士举行圆桌会议,每个圆桌会议至少3个人参加,
以下m(m<=1e6)组关系,表示u和v骑士不能在同一个圆桌会议中,
参加圆桌会议骑士必须是奇数,现求哪些骑士不可能参加任何一个会议
思路来源
指南
题解
如果可以在同一会议即连一条边,则构成简单奇环的可以在同一会议中,
奇环上任意两点均有两条点不同路径,因此在同一点双里,
找到所有点双,由于二分图无奇环,对分量判一下是不是二分图
有奇环的点双,找到奇环C和环外一点v,v到环上点u1和u2的距离为d,
u1和u2把环分成两半,一半奇一半偶,必有一半可以和d构成奇环,
因此存在奇环的连通分量,其内所有点都可为其构造出一个奇环,标记即可
答案是不在任意一个奇环上的点的个数
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
const int N=1e3+10,M=1e6+10;
using namespace std;
#define pb push_back
#define fi first
#define se second
typedef pair<int,int> P;
int n,m,ans,u,v,col[N],odd[N];
int low[N],dfn[N],cut[N],tot,belong[N],top,cnt;
P stk[M];
bool ban[N][N];
vector<int>bcc[N],e[N];
//点双板子
void dfs(int u,int fa){
low[u]=dfn[u]=++tot;
int ch=0;
for(int i=0;i<e[u].size();++i){
int v=e[u][i];
if(!dfn[v]){
stk[++top]=P(u,v);//记录当前BCC的边
dfs(v,u);
ch++;//从u这里向下dfs的子树的数量
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){//割点u
cut[u]=1;//有一个 u就是割点
++cnt;bcc[cnt].clear();
for(;;){
P x=stk[top--];
int y=x.fi,z=x.se;
if(belong[y]!=cnt){
bcc[cnt].pb(y);
belong[y]=cnt;
}
if(belong[z]!=cnt){
bcc[cnt].pb(z);
belong[z]=cnt;
}
if(y==u && z==v)break;
}
}
}
else if(v!=fa && dfn[v]<dfn[u]){
stk[++top]=P(u,v);
low[u]=min(low[u],dfn[v]);
}
}
if(fa==-1 && ch==1)cut[u]=0;//特判根节点 有两棵及以上子树
}
bool bip(int u,int c,int bc){
col[u]=c;
for(int i=0;i<e[u].size();++i){
int v=e[u][i];
if(belong[v]!=bc)continue;//不在同一点双里
if(col[v]==c)return 0;
if(col[v]==-1 && !bip(v,!c,bc))return 0;
}
return 1;
}
void findbcc(int n){
memset(dfn,0,sizeof dfn);
memset(cut,0,sizeof cut);
memset(belong,0,sizeof belong);
tot=top=cnt=0;
for(int i=1;i<=n;++i){
if(!dfn[i]){
dfs(i,-1);
}
}
}
int main(){
//freopen("my.out","w",stdout);
while(~scanf("%d%d",&n,&m)){
if(!n && !m)break;
for(int i=1;i<=n;++i){
e[i].clear();
}
memset(ban,0,sizeof ban);
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
ban[u][v]=ban[v][u]=1;
}
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(!ban[i][j]){
//printf("(%d,%d)\n",i,j);
e[i].pb(j);
e[j].pb(i);
}
}
}
findbcc(n);
memset(odd,0,sizeof odd);
for(int i=1;i<=cnt;i++){
memset(col,-1,sizeof col);
for(int j=0;j<bcc[i].size();++j){
int u=bcc[i][j];
belong[u]=i;//割点出现在多个点双里
}
if(!bip(bcc[i][0],1,i)){
for(int j=0;j<bcc[i].size();++j){
int u=bcc[i][j];
//printf("%d ",u);
odd[u]=1;
}
}
bcc[i].clear();
}
ans=n;
for(int i=1;i<=n;++i){//i位于奇圈内 顺便初始化
if(odd[i])ans--;
}
printf("%d\n",ans);
}
return 0;
}