bzoj2841
边双联通分量
具体详解蓝书上十分详细,因为必须是奇数个人坐在一起,那么一个人如果能选上,就必须处在一个简单奇圈中。而奇圈也是一个边双联通分量,所以我们先把边双联通分量都挖出来,然后进行二分图染色。
奇圈不能被二分图染色,所以标记所有不能被染色的点,减去就是答案
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N = 100010; int n, top, cnt, tot, color, ans, m; int dfn[N], low[N], st[N], vis[N], col[N], mark[N], head[N]; struct edge { int nxt, to; } e[N << 4]; vector<int> G[N]; void link(int u, int v) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; } void tarjan(int u, int last) { dfn[u] = low[u] = ++tot; st[++top] = u; for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != last) { if(!dfn[e[i].to]) tarjan(e[i].to, u); if(dfn[e[i].to] != -1) low[u] = min(low[u], low[e[i].to]); } if(low[u] == dfn[u]) { ++color; while(st[top + 1] != u) { dfn[st[top]] = -1; col[st[top]] = color; --top; } } } bool dfs(int u, int c) { vis[u] = c; for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(vis[v] == c) return false; if(!vis[v]) if(!dfs(v, c ^ 1)) return false; } return true; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) { int u, v; scanf("%d%d", &u, &v); link(u, v); link(v, u); } for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i, 0); for(int i = 1; i <= n; ++i) for(int j = head[i]; j; j = e[j].nxt) if(col[e[j].to] == col[i]) { G[e[j].to].push_back(i); G[i].push_back(e[j].to); } memset(vis, -1, sizeof(vis)); for(int i = 1; i <= n; ++i) if(vis[i] == -1 && !dfs(i, 0)) mark[col[i]] = 1; ans = n; for(int i = 1; i <= n; ++i) if(mark[col[i]]) --ans; printf("%d\n", ans); return 0; }