题目大意
有n个骑士,m对憎恨关系,每个憎恨的骑士不会坐在一起。
现在要将尽量多的奇数个人安排到一个圆桌上参加会议,将永远无法参加会议的骑士踢出骑士团。
求最少要的踢出多少骑士。
解题思路
我们将没有憎恨关系的两个骑士连一条边,即作原图的补图。
根据题意,题目就是求出最长的奇环,用n减去长度就是答案。
这里我们先构造两个引理。
引理1:两个骑士属于不同的点双联通分量,那么它们将永远无法一起参加会议。
证明:(反证法)
设两个骑士为x和y,分别属于点双联通分量c[x]和c[y](c[x] != c[y])。
假设它们可以一起参加,即有一条同时包含x,y的奇环。
因为x和y在同一环中,所以无论删去哪个节点,都有x到y的路径。
而c[x]和c[y]中删掉一个点也是也是联通的,所以c[x]和c[y]组成的图也是点双联通分量。
所以假设不成立。
证毕。
引理2:一个点双联通分量中存在奇环,则该点双联通分量中任意两个点都被任意一个包含。
证明:
设x和y属于同一个点双联通分量且在同一个奇环中,z为该双联通分量中的任意一点。
那么x到y的路径加上x经过z到y的路径即为一个环。
因为一个奇数只能分成偶数+奇数,所以x到y的两条路径为一奇一偶。
无论x经过z到y的路径长为多少,所以一定有包含x,y和z的奇环。
证毕。
所以说,我们用tarjan跑出该图中的点双联通分量,如果这个点双联通中有奇环,那么这个点双联通分量中的骑士都不会被踢出,把他标记掉。
最后统计一下没有被标记的人数。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 1005
#define MAXM 2000005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int p = 10000007;
int lowbit(int x){ return x & -x; }
int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p; } return x; }
stack<int> sta;
vector<int> dcc[MAXN];
int cnt, num, tot, now;
int head[MAXN], nxt[MAXM], to[MAXM];
int dfn[MAXN], low[MAXN], c[MAXN], col[MAXN];
bool res, g[MAXN][MAXN], able[MAXN];
void init(){
cnt = num = tot = 0;
clr(head, -1);
clr(g, 0);
clr(dfn, 0);
clr(low, 0);
clr(able, 0);
clr(c, 0);
}
void add(int u, int v){
nxt[cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
cnt++;
}
void tarjan(int u, int root){
dfn[u] = low[u] = ++num;
sta.push(u);
if(u == root && head[u] == 0){
dcc[++tot].pb(u);
return;
}
for(int e = head[u]; e != -1; e = nxt[e]){
int v = to[e];
if(!dfn[v]){
tarjan(v, root);
low[u] = min(low[u], low[v]);
if(dfn[u] <= low[v]){
tot++;
while(1){
int t = sta.top();
dcc[tot].pb(t);
sta.pop();
if(t == v) break;
}
dcc[tot].pb(u);
}
}
else low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u, int color){
col[u] = color;
for(int e = head[u]; e != -1; e = nxt[e]){
int v = to[e];
if(c[v] != now) continue;
if(!col[v]) dfs(v, 3 - color);
else if(col[v] == color){
res = 1;
return;
}
}
}
int main(){
int n, m;
while(~scanf("%d%d", &n, &m) && n){
init();
rep(i, 1, n) dcc[i].clear();
while(!sta.empty()) sta.pop();
rep(i, 1, m){
int u, v;
scanf("%d%d", &u, &v);
g[u][v] = g[v][u] = 1;
}
rep(i, 1, n)
rep(j, i + 1, n)
if(!g[i][j]) add(i, j), add(j, i);
rep(i, 1, n)
if(!dfn[i]) tarjan(i, i);
rep(i, 1, tot){
now = i;
rep(j, 0, SZ(dcc[i]) - 1) c[dcc[i][j]] = now, col[dcc[i][j]] = 0;;
res = 0;
dfs(dcc[i][0], 1);
if(res)
rep(j, 0, SZ(dcc[i]) - 1) able[dcc[i][j]] = 1;
}
int ans = 0;
rep(i, 1, n)
if(!able[i]) ans++;
printf("%d\n", ans);
}
return 0;
}