题目链接
本题是2005年ICPC欧洲区域赛中欧赛区的A题
题意
有n个骑士经常举行圆桌会议,商讨大事。每次圆桌会议至少应有3个骑士参加,且相互憎恨的骑士不能坐在圆桌旁的相邻位置。如果发生意见分歧,则需要举手表决,因此参加会议的骑士数目必须是奇数,以防止赞同和反对票一样多。知道哪些骑士相互憎恨之后,你的任务是统计有多少个骑士不可能参加任何一个会议。
分析
将不相互憎恨的骑士连一条无向边建图,如果对一个结点(骑士)能找到含奇数个结点且结点不重复的圈,则此骑士至少可以参加一个会议。因此需要先找出所有双连通分量,二分图不存在长度为奇数的环,所以只需要关注那些不是二分图的双连通分量。更进一步,如果一个双连通分量不是二分图,它至少含有一个长度为奇数的环。
一个双连通分量含有一个长度为奇数的环
C
C
C,分析一下此分量中不在环C上的某个结点
v
v
v:
根据双连通性,
C
C
C中应该存在两个不同的结点
u
1
u_1
u1、
u
2
u_2
u2,使得从
v
v
v出发有两条不相交路径(除起点外无公共结点),分别到
u
1
u_1
u1和
u
2
u_2
u2。由于在
C
C
C中,从
u
1
u_1
u1到
u
2
u_2
u2的两条路的长度一奇一偶,总能构造出一个经过
v
v
v的长度为奇数的环。
至此,主算法清晰了:对于每个连通分量的每个双连通分量
B
B
B,若它不是二分图,给
B
B
B中所有结点标记为“在长度为奇数的环上”,最后遍历每个结点,统计出无标记的结点数就是答案。
AC 代码
#include <iostream>
#include <cstring>
using namespace std;
#define N 1010
int s[N*N][2], bcc[N][N], g[N][N], c[N], t[N], pre[N], bn[N], color[N], clk, cc, m, n, p; bool off[N][N], ex[N];
int dfs(int u, int fa = -1) {
int low = pre[u] = ++clk;
for (int i=0, v; i<c[u]; ++i) if (!pre[v = g[u][i]]) {
s[p][0] = u; s[p++][1] = v;
int lowv = dfs(v, u); low = min(low, lowv);
if (lowv >= pre[u]) {
t[++cc] = 0;
while (true) {
int x = s[--p][0], y = s[p][1];
if (bn[x] != cc) bcc[cc][t[cc]++] = x, bn[x] = cc;
if (bn[y] != cc) bcc[cc][t[cc]++] = y, bn[y] = cc;
if (x == u && y == v) break;
}
}
} else if (pre[v] < pre[u] && v != fa) {
s[p][0] = u; s[p++][1] = v; low = min(low, pre[v]);
}
return low;
}
bool bipartite(int u, int b) {
for (int i=0, v; i<c[u]; ++i) if (bn[v = g[u][i]] == b) {
if (color[v] == color[u]) return false;
if (!color[v]) {
color[v] = 3-color[u];
if (!bipartite(v, b)) return false;
}
}
return true;
}
int solve() {
memset(off, 0, sizeof(off)); memset(c, p = 0, sizeof(c)); memset(ex, 1, sizeof(ex));
memset(pre, clk = 0, sizeof(pre)); memset(bn, cc = 0, sizeof(bn));
while (m--) {
int u, v; cin >> u >> v;
off[u][v] = off[v][u] = 1;
}
for (int u=1; u<=n; ++u) for (int v=u+1; v<=n; ++v) if (!off[u][v]) g[u][c[u]++] = v, g[v][c[v]++] = u;
for (int u=1; u<=n; ++u) if (!pre[u]) dfs(u);
for (int i=1; i<=cc; ++i) {
for (int j=0; j<t[i]; ++j) bn[bcc[i][j]] = i, color[bcc[i][j]] = 0;
int u = bcc[i][0]; color[u] = 1;
if (!bipartite(u, i)) for (int j=0; j<t[i]; ++j) ex[bcc[i][j]] = 0;
}
int ans = 0;
for (int i=1; i<=n; ++i) if (ex[i]) ++ans;
return ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n >> m && n) cout << solve() << endl;
return 0;
}