这道题debug了两个下午终于AC了(还是对拍好用),心中十分激动~~
首先不得不说,不看题解我是无论如何也想不出怎么建图的,刚开始直接按仇恨关系建图怎么搞也好不出来。
题解说,要建互补图!就是如果两个骑士能挨一块,就连一条边。然后tarjan求点双联通分量。为什么求点双联通分量呢?因为每一个会议都要围成一个环,而一个人不能同时参加两个会议,所以对于两个点u, v,要有至少两条完全不同的路能从u到v,那就是求点双联通分量了。
点双联通分量的求法和找割点很像,不过还有维护一个栈。如果这个节点第一次遍历,就放入栈中;如果low[v] >= dfn[u],说明u可能是割点(因为可能是根节点),但这并没有什么关系,这时候开始弹栈,直到st.top == v,因为u可能同时在多个点双中,所以u不能弹出,但还要把u加入这个点双中。记录所有点双,拿一个vector就行。
然后题中说,每一个会议参加的人数必须是奇数,所以我们要在点双联通分量里找奇环。有一个结论:如果一个点双联通分量中存在一个奇环,那么联通分量中任意一个点都在奇环上。这个好证:
假设奇环是x->B->y->A->x。然后对于两桶分量中的节点u:有两个环:u->x->B->y->u和u->x->A->y->u。又因为x->B->y和x->A->y中一定有一条路径是奇数的,那么包含u的两个环中一定有一个是奇环。
那么如何找奇环呢?判断二分图时有这么个结论:如果一张图不存在奇环,那么就是二分图。做法就是黑白染色,如果u走到得一个节点v已经被染过色了,且col[u] == col[v],那么就存在奇环。
然后如果这个联通分量中没有奇环,这些人就都无法参加任何一个会议。
还有一件事,这道题拿邻接矩阵存图比较方便一些。

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 1e3 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, m; 38 int v[maxn][maxn]; 39 40 stack<int> st; 41 int dfn[maxn], low[maxn], cnt = 0; 42 vector<int> vd[maxn]; 43 int cvd = 0; 44 void tarjan(int now, int fa) //求点双 45 { 46 dfn[now] = low[now] = ++cnt; 47 st.push(now); 48 for(int i = 1; i <= n; ++i) 49 { 50 if(v[now][i] && now != i) 51 { 52 if(!dfn[i]) 53 { 54 tarjan(i, now); 55 low[now] = min(low[now], low[i]); 56 if(low[i] >= dfn[now]) 57 { 58 int x; ++cvd; 59 do 60 { 61 x = st.top(); st.pop(); 62 vd[cvd].push_back(x); 63 }while(x != i); 64 vd[cvd].push_back(now); 65 } 66 } 67 else if(i != fa) low[now] = min(low[now], dfn[i]); 68 } 69 70 } 71 } 72 73 bool vis[maxn]; 74 int mak[maxn], col[maxn]; 75 bool dfs(int now, int flg) //染色 76 { 77 mak[now] = flg; 78 for(int i = 1; i <= n; ++i) 79 { 80 if(v[now][i] && now != i && col[now] == col[i]) 81 { 82 if(mak[i] == -1) if(dfs(i, flg ^ 1)) return 1; 83 if(mak[i] == mak[now]) return 1; 84 } 85 } 86 return 0; 87 } 88 89 void init() 90 { 91 for(int i = 0; i < maxn; ++i) 92 for(int j = 0; j < maxn; ++j) v[i][j] = 1; 93 for(int i = 0; i < maxn; ++i) vd[i].clear(); 94 while(!st.empty()) st.pop(); 95 Mem(dfn, 0); Mem(low, 0); 96 cnt = cvd = 0; 97 Mem(vis, 0); Mem(col, 0); 98 } 99 100 int main() 101 { 102 while(scanf("%d%d", &n, &m) && n && m) 103 { 104 init(); 105 for(int i = 1; i <= m; ++i) 106 { 107 int x = read(), y = read(); 108 v[x][y] = v[y][x] = 0; 109 } 110 for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i, 0); 111 for(int i = 1; i <= cvd; ++i) 112 { 113 Mem(mak, -1); 114 for(int j = 0; j < (int)vd[i].size(); ++j) col[vd[i][j]] = i; 115 if(dfs(vd[i][0], 0)) 116 for(int j = 0; j < (int)vd[i].size(); ++j) vis[vd[i][j]] = 1; 117 } 118 int ans = 0; 119 for(int i = 1; i <= n; ++i) ans += ((int)vis[i]) ^ 1; 120 write(ans); enter; 121 } 122 }