1. 程式人生 > 其它 >2020-2021年度第二屆全國大學生演算法設計與程式設計挑戰賽(冬季賽)——熱身賽 D題

2020-2021年度第二屆全國大學生演算法設計與程式設計挑戰賽(冬季賽)——熱身賽 D題

2020-2021年度第二屆全國大學生演算法設計與程式設計挑戰賽(冬季賽)——熱身賽 D題

連結:http://vj.saikr.com/contest/10/problem/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;
}