题意
求图上有多少点不存在任意一个奇环上。
思路
首先有一个 很容易发现 的性质:如果一个点双中有一个奇环,那么整个点双的点都至少在一个奇环上。反正我是看了蓝书才知道的。
然后算法非常简单,求一遍点双,每个点双找奇环。
注意点双和Dfs求奇环的打法,还有vector很容易忘记初始化。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 2010;
const int M = 6e6+10;
int n, m, e, point[N];
bool ban[N][N];
struct EDGE{
int nxt, v;
}edge[M];
int dfn[N], low[N], idx, stk[N], st, rt, dccn, col[N], ans;
vector<int> dcc[N];
bool now[N], odd[N];
void add_edge(int u, int v)
{
edge[++e] = (EDGE){point[u], v};
point[u] = e;
}
void Tarjan(int u)
{
dfn[u] = low[u] = ++idx;
if (rt == u && point[u] == -1){
dccn++;
dcc[dccn].clear();
dcc[dccn].push_back(u);
return;
}
stk[++st] = u;
for (int i = point[u]; i != -1; i = edge[i].nxt){
int v = edge[i].v;
if (!dfn[v]){
Tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]){
dccn++;
dcc[dccn].clear();
dcc[dccn].push_back(u);
while (1){
int w = stk[st];
dcc[dccn].push_back(w);
st--;
if (w == v){ // 做到子树全部弹出为止,不是u,不然v的兄弟也会被弹出
break;
}
}
}
}
else{
low[u] = min(low[u], dfn[v]);
}
}
}
bool Dfs(int u)
{
for (int i = point[u]; i != -1; i = edge[i].nxt){
int v = edge[i].v;
if (!now[v]){
continue;
}
if (col[v] && col[v] == col[u]){
return true;
}
else if (!col[v]){
col[v] = 3-col[u];
if (Dfs(v)){
return true;
}
}
}
return false;
}
int main()
{
while (scanf("%d%d", &n, &m) == 2 && n+m){
memset(ban, 0, sizeof(ban));
for (int i = 1; i <= m; i++){
int x, y;
scanf("%d%d", &x, &y);
ban[x][y] = ban[y][x] = 1;
}
memset(point, -1, sizeof(point)); e = -1;
for (int i = 1; i <= n; i++)
for (int j = i+1; j <= n; j++)
if (!ban[i][j]){
add_edge(i, j);
add_edge(j, i);
}
memset(dfn, 0, sizeof(dfn));
idx = st = dccn = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i]){
rt = i;
Tarjan(i);
}
memset(odd, 0, sizeof(odd));
for (int i = 1; i <= dccn; i++){
memset(now, 0, sizeof(now));
memset(col, 0, sizeof(col));
for (int j = 0, sz = dcc[i].size(); j < sz; j++)
now[dcc[i][j]] = 1; // cout << dcc[i][j] << " ";
col[dcc[i][0]] = 1;
if (Dfs(dcc[i][0])){
// cout << "!" << endl;
for (int j = 0, sz = dcc[i].size(); j < sz; j++)
odd[dcc[i][j]] = 1;
}
}
ans = 0;
for (int i = 1; i <= n; i++)
if (!odd[i]){
ans++;
}
printf("%d\n", ans);
}
return 0;
}
/*
5 10
3 1
2 4
2 1
1 1
1 3
1 1
2 1
1 3
1 1
4 3
------------
0
点双求错了还能A掉讨论的大部分样例
*/