UVa 1364 - Knights of the Round Table (无向图双连通分量 tarjan)
题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=0&problem=4110&mosmsg=Submission+received+with+ID+26571544
如果两个骑士不互相讨厌,即可以相邻,则参加会议的骑士在无向图中组成简单奇圈。简单圈上的所有结点必定属于同一个双连通分量,所以要先找出所有的双连通分量。而二分图是没有奇圈的,所以找出不是二分图的双连通分量,对双连通分量二分图染色判断即可。可以证明,存在奇圈的双连通分量中的所有节点都在奇圈上。给这些双连通分量中的点打上标记统计答案即可。
时间复杂度 \(O(n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1010;
const int maxm = 1000010;
int n, m;
int g[maxn][maxn];
int h[maxn], cnt = 0;
struct E{
int from, to, next;
}e[maxm << 1];
void add(int u, int v){
e[++cnt].to = v;
e[cnt].from = u;
e[cnt].next = h[u];
h[u] = cnt;
}
stack<E> s;
vector<int> bcc[maxn];
int st[maxn], iscut[maxn], bccno[maxn], low[maxn], dfn = 0, bcc_cnt = 0;
int dfs(int u, int par){
int lowu = st[u] = ++dfn;
int child = 0;
for(int i = h[u] ; i != -1 ; i = e[i].next){
int v = e[i].to;
E e = (E){u, v, -1};
if(!st[v]){
s.push(e);
++child;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if(lowv >= st[u]){
iscut[u] = 1;
bcc_cnt++;
bcc[bcc_cnt].clear();
for(;;){
E x = s.top(); s.pop();
if(bccno[x.from] != bcc_cnt){ bcc[bcc_cnt].push_back(x.from); bccno[x.from] = bcc_cnt; }
if(bccno[x.to] != bcc_cnt){ bcc[bcc_cnt].push_back(x.to); bccno[x.to] = bcc_cnt; }
if(x.from == u && x.to == v) break;
}
}
} else if(st[v] < st[u] && v != par){ // 反向边更新
s.push(e);
lowu = min(lowu, st[v]);
}
}
if(par == 0 && child == 1) iscut[u] = 0;
low[u] = lowu;
return low[u];
}
void tarjan(){
memset(st, 0, sizeof(st));
memset(iscut, 0, sizeof(iscut));
memset(bccno, 0, sizeof(bccno));
dfn = bcc_cnt = 0;
for(int i = 1 ; i <= n ; ++i){
if(!st[i]) dfs(i, 0);
}
}
int odd[maxn], color[maxn];
bool bipartite(int u, int b){
for(int i = h[u] ; i != -1 ; i = e[i].next){
int v = e[i].to;
if(bccno[v] != b) continue;
if(color[v] == color[u]) return false;
if(!color[v]){
color[v] = 3 - color[u];
if(!bipartite(v, b)) return false;
}
}
return true;
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
while(scanf("%d%d", &n, &m) == 2 && n){
memset(h, -1, sizeof(h)); cnt = 0; dfn = 0;
memset(g, 0, sizeof(g));
int u, v;
for(int i = 1 ; i <= m ; ++i){
scanf("%d%d", &u, &v);
g[u][v] = g[v][u] = 1;
}
for(int i = 1 ; i <= n ; ++i){
for(int j = i + 1 ; j <= n ; ++j){
if(!g[i][j]) {
add(i, j); add(j, i);
}
}
}
tarjan();
memset(odd, 0, sizeof(odd));
for(int i = 1 ; i <= bcc_cnt ; ++i){
memset(color, 0, sizeof(color));
for(int j = 0 ; j < bcc[i].size() ; ++j) bccno[bcc[i][j]] = i;
int u = bcc[i][0];
color[u] = 1;
if(!bipartite(u, i)){
for(int j = 0 ; j < bcc[i].size() ; ++j) odd[bcc[i][j]] = 1;
}
}
int ans = n;
for(int i = 1 ; i <= n ; ++i) if(odd[i]) --ans;
printf("%d\n", ans);
}
return 0;
}