题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5409
题意:
一些点,一些边。
要求找出那些点,与任何其他任意多个点都不能构成一个简单奇环。
输出这些点的个数。
思路:
好难~
一开始就卡住,想这个怎么去选点都会变成分类讨论。
题解直接取了原图的补图,然后判断哪些点不再奇圈上。
对于一个双连通图,若中间存在奇圈,则可以证明任意两点之间一定存在奇圈,详见大白书P136。所以问题就变成了求哪些点不在这些有奇圈的双连通分量中。对于一个双连通分量含不含有奇圈,只需要判断它是不是二分图、DFS即可。
双连通分量求法与求割顶和桥略有不同,需要用到栈,而且栈里面存的是边。每次找到一个合法情况就一直弹栈把所有在这个双连通分量中的边弹出来。刚开始用栈存点WA了几发,发现存点的话情况会变得极其复杂,还是存边好,因为对于两个点双连通分量,会有点重、但不会有边重的情况。 PS:如果是求边双连通分量,就不需要那么麻烦,因为它连点都不会重。
大白书上的题解会RE哦。
源码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <stack>
using namespace std;
#define gmin(a,b) ((a) < (b) ? (a) : (b))
#define gmax(a,b) ((a) > (b) ? (a) : (b))
const int MAXN = 1000 + 5;
int graph[MAXN][MAXN];
vector<int>lin[MAXN];
int n, m;
int pre[MAXN], low[MAXN], clock;
int bccno[MAXN], bcc_cnt;
int out[MAXN], color[MAXN];
struct Edge
{
int u, v;
Edge(){}
Edge(int _u, int _v){u = _u, v = _v;}
};
stack<Edge>sta;
void Edge_init()
{
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
graph[i][j] = 1;
int u, v;
for(int i = 1 ; i <= m ; i++){
scanf("%d%d", &u, &v);
graph[u][v] = graph[v][u] = 0;
}
for(int i = 1 ; i <= n ; i++)
lin[i].clear();
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= n ; j++){
if(i != j && graph[i][j])
lin[i].push_back(j);
}
}
}
int bipartite(int u, int op)
{
int ans = 1;
color[u] = op;
for(int j = 0 ; j < (int)lin[u].size() ; j++){
int v = lin[u][j];
if(bccno[v] != bccno[u]) continue;
if(color[v] == color[u]) return 0;
if(color[v] == 0) ans = ans * bipartite(v, -op);
}
return ans;
}
void DFS_bcc(int u, int fa)
{
pre[u] = low[u] = ++clock;
for(int i = 0 ; i < (int)lin[u].size() ; i++){
int v = lin[u][i];
// printf("u = %d, v = %d\n", u, v);
Edge e = Edge(u,v);
if(pre[v] == 0){
// printf("u = %d, v = %d\n", u, v);
sta.push(e);
DFS_bcc(v, u);
low[u] = gmin(low[u], low[v]);
if(low[v] >= pre[u]){
++bcc_cnt;
while(!sta.empty()){
Edge t = sta.top(); sta.pop();
// printf("t.u = %d, t.v = %d\n", t.u, t.v);
if(bccno[t.u] != bcc_cnt){bccno[t.u] = bcc_cnt;}
if(bccno[t.v] != bcc_cnt){bccno[t.v] = bcc_cnt;}
if(t.u == u && t.v == v)
break;
}
// printf("u = %d, bcc_cnt = %d\n", u, bcc_cnt);
memset(color, 0, sizeof(color));
if(!bipartite(u, 1)){
for(int i = 1 ; i <= n ; i++){
if(bccno[i] == bcc_cnt){
out[i] = 1;
}
}
}
}
}
else if(pre[v] < pre[u] && v != fa){
sta.push(e);
low[u] = gmin(low[u], pre[v]);
}
}
}
void BCC()
{
bcc_cnt = 0;
memset(pre, 0, sizeof(pre));
clock = 0;
while(!sta.empty()) sta.pop();
memset(bccno, 0, sizeof(bccno));
for(int i = 1 ; i <= n ; i++){
if(pre[i] == 0){
DFS_bcc(i, -1);
}
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF && n + m){
Edge_init();
memset(out, 0, sizeof(out));
BCC();
int ans = 0;
for(int i = 1 ; i <= n ; i++)
if(!out[i]) ans++;
printf("%d\n", ans);
}
return 0;
}