[SP2878]KNIGHTS - Knights of the Round Table
一.前言
脑阔完全转不动……都不知道要干些什么,题目链接
二.思路
首先它是圆桌骑士开会,所以他们开会的样子是一个环,并且避免平票需要只有奇数个人(不能一人开会)。将仇恨关系建图不好做,于是建立补图,将可以坐在一起的骑士连边,这样形成了一个可能的座位形状?然后去里面找尽可能多的奇环。输出不被任何一个奇环包括的骑士。
然后到这里我们会有一个结论如下:
对于一个点双连通分量,里面若是有一个奇环,那么该点双连通分量里面所有的点都可以被包括在奇环之中
虽然我也不知道为什么会出现这个结论(好像写边双也可)就象征性的推导一下:
首先点双连通分量由一些环组成
假设在点双连通分量之中有环 a ,若 a 是奇环,那么 a 中所有点都可以开会,不影响答案
若 a 是偶环,那么将它和点双中的奇环链接成一个大奇环就可以了。
我也不知道是不是对的hhh,感性理解一下……同样我也不好解释奇环和点双的关系,硬要说的话,就是 tarjan 缩点之后不会有环,换而言之所有的环都在点双连通分量之中,由于这个结论很方便,就这么写了
现在需要判断一个点双里面有没有奇环。对于一个没有奇环的图来说,他一定是一个二分图(有了奇环就构不成二分图),转化为判断一个点双是不是二分图,使用二分图黑白染色,即将一个点周围的点全部染上相异的颜色,有冲突就不是二分图,也就是有奇环。
这样写就可以了。
三.CODE
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#include<cmath>
#include<cstring>
using namespace std;
int read(){
char ch=getchar();
int res=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
return res*f;
}
const int MAXN=1e6+5;
int n,m;
bool hate[1005][1005];
int head[1005],ne[MAXN],to[MAXN],tot;
void add(int x,int y){
to[++tot]=y,ne[tot]=head[x],head[x]=tot;
}
void build(){
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(!hate[i][j])add(i,j),add(j,i);
}
int dfn[1005],low[1005],date;
int st[1005],top,rs[1005];
bool vis[1005],fl,wdnmd[1005];
int ans;
bool check(int x,int fa){
if(fl)return 1;
for(int i=head[x];i;i=ne[i]){
int v=to[i];
if(v==fa||!vis[v])continue;
if(rs[v]==-1){
rs[v]=rs[x]^1;//染色
check(v,x);
if(fl)return 1;
}
else if(rs[v]!=(rs[x]^1))return (fl=1);//冲突
}
return 0;
}
void dfs(int x,int fa){//tarjan
dfn[x]=low[x]=++date;
st[++top]=x;
for(int i=head[x];i;i=ne[i]){
int v=to[i];
if(!dfn[v]){
dfs(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]){//出现点双
int count=1;
fl=0;
memset(vis,0,sizeof(vis));
memset(rs,-1,sizeof(rs));
vis[x]=1;//将当前点双之中的全部打上标记
rs[x]=0;
while(st[top]!=v)vis[st[top--]]=1,count++;
vis[v]=1,top--,count++;
if(count>=3&&check(x,x))for(int i=1;i<=n;++i)if(vis[i])wdnmd[i]=1;
//二分图染色
}
}
else low[x]=min(low[x],dfn[v]);
}
}
void clean(){
memset(hate,0,sizeof(hate));
memset(head,0,sizeof(head));
tot=0;ans=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(wdnmd,0,sizeof(wdnmd));
}
int main(){
while(1){
n=read();m=read();
if(n==m&&n==0)break;
clean();//多测清空
for(int i=1,x,y;i<=m;++i){
x=read();y=read();
hate[x][y]=hate[y][x]=1;
}
build();//建立补图
for(int i=1;i<=n;++i)if(!dfn[i]){//可能会不连通,找点双
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
date=top=0;
dfs(i,i);
}
for(int i=1;i<=n;++i)ans+=(wdnmd[i]==0);//加上不被点双标记的
printf("%d
",ans);
}
return 0;
}