2020-2021年度第二屆全國大學生演算法設計與程式設計挑戰賽(冬季賽)——熱身賽 D題
阿新 • • 發佈:2022-03-17
2020-2021年度第二屆全國大學生演算法設計與程式設計挑戰賽(冬季賽)——熱身賽 D題
題意:
給出一個圖,分山頂山谷,只有山頂和山谷之間連邊,顯然得圖為二分圖。
從左側點出發,輪流走,直到不能走,停在頂則Cong勝,反之則Ni勝,自然可想到博弈論方向。
分析:
我們對其跑一個最大匹配,其增廣路徑為匹配邊,非匹配邊交替。
- 若一條連續增廣路徑如圖,左側點數大於右側點數,則無論如何,從左側出發,最後必然會落在左側。
- 若一條連續最長增廣路徑如圖,左側點數小於等於右側點數,則無論如何,從左側出發,最後必然落在右側。
我們建立超級源點s和超級匯點t,對該二分圖跑一個最大流。
對於偶數個邊,必然在增廣路徑兩頭有一個左側的點沒有流量,所以我們從超級源點按照容量餘下1的邊遍歷(從右向左為反邊的流量容量),所有遍歷到的左側點為敗點。
對於奇數個邊,則增廣路徑中的左側點必然都有流量,所以我們從超級源點遍歷時遍歷不到,故未被遍歷到的點都為勝點。
結論
我們對原二分圖用超級源匯跑一個最大流,在從源點bfs所有流量容量為1的邊。所有被標記的左點都為Cong贏,沒有被標記的左點都為Ni贏。
/*********************************** dinic最大流思路: 不斷圖分層,保證dfs過程中,不會出現環等現象 在一次分層中,用dfs一次性搜尋多條增廣路。 就是向下搜,搜到t為止,然後返回流量。 當前節點最大流量為的下屬分支所有流量的返回最大流量。 下屬分支返回的流量為min(通往該分支邊的最大流量,分支的下屬總流量) 當dfs一次後,一定會有某些邊被榨乾,改變分層圖。 再次分層圖,重複上述過程。 分層圖用bfs實現。如果bfs不到終點,則結束演算法。 ************************************/ #include<iostream> #include<cstdio> #include<cstdlib> #include<queue> using namespace std; typedef long long ll; typedef long long ii; const ll maxn = 1e5 + 50; const ll maxm = 1e5 + 50; const ll INF = ll(1) << 50; struct e { ll next, to, flow; } edge[maxm * 2]; int head[maxn * 2], tot = 0; void add(ll u, ll v, ll flow) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].flow = flow; head[u] = tot++; edge[tot].to = u; edge[tot].next = head[v]; edge[tot].flow = 0; head[v] = tot++; } ll n, m, s, t; ll dis[maxn * 2]; queue<ll> q; ll cur[maxn * 2]; ll dfs(ll u, ll lim) { if (u == t)return lim; ll flow = 0; for (ll i = cur[u]; ~i; i = edge[i].next) { cur[u] = i; ll v = edge[i].to; if (dis[v] != dis[u] + 1 || edge[i].flow == 0) { continue; } ll f = dfs(v, min(edge[i].flow, lim)); flow += f; lim -= f; edge[i].flow -= f; edge[i ^ 1].flow += f; if (lim == 0)break; } return flow; } ll bfs() { while (!q.empty())q.pop(); for (ll i = 0; i <= n * 2 + 1 ; i++) { dis[i] = INF; cur[i] = head[i]; } dis[s] = 0; q.push(s); while (!q.empty()) { ll u = q.front(); q.pop(); for (ll i = head[u]; ~i; i = edge[i].next) { ll v = edge[i].to; if (edge[i].flow == 0 || dis[v] != INF)continue; dis[v] = dis[u] + 1; q.push(v); } } if (dis[t] == INF)return 0; else return 1; } void dfs4(int u, int p) { } void init() { for (ll i = 0; i <= n * 2 + 50; i++) { head[i] = -1; } } int check[maxn * 2]; void bfs2() { queue<int> q; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (edge[i].flow == 1 && check[v] != 1 ) { q.push(v); check[v] = 1; } } } } int main() { scanf("%lld%lld", &n, &m); init(); s = 0; t = 2 * n + 1; ll a, b; for (int i = 1; i <= n; i++) { add(s, i, 1); add(i + n, t, 1); } for (int i = 0; i < m; i++) { scanf("%lld%lld", &a, &b); add(a, b + n, 1); //add(b + n, a, 1); } ll maxflow = 0; while (bfs()) { maxflow += dfs(s, INF); } bfs2(); for (int i = 1; i <= n; i++) { if (check[i] == 0) { puts("Ni"); } else { puts("Cong"); } } return 0; }