Codeforces Round #523 (Div. 2) E. Politics (費用流 + 巧妙建圖)

題目鏈接:http://codeforces.com/contest/1061/problem/E

題目大意:在一個國家中有n個城市,現在有兩個人在競選總統,第一個人所設立的首都爲x,第二個人所設立的首都爲y,每個人都提出了一種建設國家的方法,即以他們各自所設立的首都作爲根節點,建立n-1條道路,使得這個國家變成一棵樹。

這n個城市都可以建立港口,第 i 個城市建立港口的收益爲val[i]。

同時第一個人提出了q1個要求,每個要求都要求第ki個城市的子樹中有xi個城市建立了港口;

第二個人也提出了q2個要求,每個要求也要求第ki個城市的子樹中有xi個城市建立了港口。

現在問你要在哪些城市建立港口,在滿足這兩個人所有要求的前提下收益最大,輸出最大的收益,如果無法建成,就輸出-1。

題目思路:這個由於有一定的限制條件,只能選定一定的城市來建立港口的前提下使得收益最大,所以我們可以考慮用費用流來解決這個問題(只要將收益變成負數,求出的最小費用就是最大的收益了)。

現在的難點就是該如何建圖。

由於是要同時滿足兩個人的所有要求,所以我們可以考慮將這n個城市進行拆點處理。

每個城市的入點就從源點連入第一個人要求這個城市的子樹中所需要建設的港口數量,出點就向匯點連出第二個人要求這個城市的子樹中所需要建設的港口數量。

但由於題目給的信息是某個點整個子樹內的信息,所以我們得先借助dfs將這些所需要的信息處理一下。

現在假設 u 這個城市的子樹所需要建設的港口爲 limit[u],假設res=\sum_{v = son[u]}limit[v]

那麼當res > limit[u]的時候,就說明這個要求是無法實現的。

否則的話,就令 limit[u] = limit[u] - res,即將其子樹中已經確定的港口數量減掉,剩下的limit[u]就是可以隨便選擇的城市數量了。

同時我們也要記錄一下,每個節點往根節點的路徑上離它最近的limit[u] > 0的節點pre,這就代表着這個點是可以爲pre這個節點提供一個可建設港口的選擇。

做完預處理之後,在建圖的時候,就可以在pre[i]的入點和pre[i]的出點直接建立一個流量爲1,費用爲-val[i]的邊。

每個點的入點從源點連入一條流量爲limit1[i],費用爲0的邊,出點就向匯點連一條流量爲limit2[i],費用爲0的邊。

跑一遍最大流量最小費用流就行了,如果最大流量不等於兩個方案所需要建設的港口總數或者兩個方案所建設的港口的總數不相等的話,就說明沒有合法的建設方案。

具體實現看代碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define all(v) v.begin(),v.end()
#define clr(a) memset(a,0,sizeof(a))
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<int, ll>P;
const double pi = acos(-1.0);
const double eps = 1e-8;
const int MX  = 1000 + 7;
const int ME = 1e4 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
//head

struct MCMF {
	int S, T;
	int tot, n;
	int st, en, maxflow, mincost;
	bool vis[MX];
	int head[MX], cur[MX], dis[MX];

	queue <int> Q;
	struct Edge {
		int v, cap, cost, nxt, flow;
		Edge() {}
		Edge(int a, int b, int c, int d) {
			v = a, cap = b, cost = c, nxt = d, flow = 0;
		}
	} E[ME], SE[ME];

	void init(int _n) {
		n = _n, tot = 0;
		for (int i = 0; i <= n; i++) head[i] = -1;
	}
	void edge_add(int u, int v, int cap, int cost) {
		E[tot] = Edge(v, cap, cost, head[u]);
		head[u] = tot++;
		E[tot] = Edge(u, 0, -cost, head[v]);
		head[v] = tot++;
	}
	bool adjust() {
		int v, min = INF;
		for (int i = 0; i <= n; i++) {
			if (!vis[i]) continue;
			for (int j = head[i]; ~j; j = E[j].nxt) {
				v = E[j].v;
				if (E[j].cap - E[j].flow) {
					if (!vis[v] && dis[v] - dis[i] + E[j].cost < min) {
						min = dis[v] - dis[i] + E[j].cost;
					}
				}
			}
		}

		if (min == INF) return false;
		for (int i = 0; i <= n; i++) {
			if (vis[i]) {
				cur[i] = head[i];
				vis[i] = false;
				dis[i] += min;
			}
		}
		return true;
	}
	int augment(int i, int flow) {
		if (i == en) {
			mincost += dis[st] * flow;
			maxflow += flow;
			return flow;
		}
		vis[i] = true;
		for (int j = cur[i]; j != -1; j = E[j].nxt) {
			int v = E[j].v;
			if (E[j].cap == E[j].flow) continue;
			if (vis[v] || dis[v] + E[j].cost != dis[i]) continue;
			int delta = augment(v, std::min(flow, E[j].cap - E[j].flow));
			if (delta) {
				E[j].flow += delta;
				E[j ^ 1].flow -= delta;
				cur[i] = j;
				return delta;
			}
		}
		return 0;
	}
	void spfa() {
		int u, v;
		for (int i = 0; i <= n; i++) {
			vis[i] = false;
			dis[i] = INF;
		}
		Q.push(st);
		dis[st] = 0; vis[st] = true;
		while (!Q.empty()) {
			u = Q.front(), Q.pop(); vis[u] = false;
			for (int i = head[u]; ~i; i = E[i].nxt) {
				v = E[i].v;
				if (E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue;
				dis[v] = dis[u] + E[i].cost;
				if (!vis[v]) {
					vis[v] = true;
					Q.push(v);
				}
			}
		}
		for (int i = 0; i <= n; i++) {
			dis[i] = dis[en] - dis[i];
		}
	}
	int zkw(int s, int t, int &ret_flow) {
		st = s, en = t;
		spfa();
		mincost = maxflow = 0;
		for (int i = 0; i <= n; i++) {
			vis[i] = false;
			cur[i] = head[i];
		}
		do {
			while (augment(st, INF)) {
				memset(vis, false, n * sizeof(bool));
			}
		} while (adjust());
		ret_flow = maxflow;
		return mincost;
	}
} F;

int n, x, y;
int val[MX];
bool flag;
vector<int>G[MX];
int limt[MX], pre[MX];

int dfs(int u, int fa, int f) {
	if (limt[u]) f = u;
	pre[u] = f;
	int res = 0, now = limt[u];
	for (auto v : G[u]) {
		if (v == fa) continue;
		res += dfs(v, u, f);
	}
	if (limt[u]) {
		if (limt[u] < res) flag = 0;
		limt[u] -= res;
		res = now;
	}
	return res;
}

int main() {
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
#endif
	scanf("%d%d%d", &n, &x, &y);
	for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
	for (int i = 1; i < n; i++) {
		int u, v; scanf("%d%d", &u, &v);
		G[u].pb(v); G[v].pb(u);
	}
	for (int i = 1; i < n; i++) {
		int u, v; scanf("%d%d", &u, &v);
		G[u + n].pb(v + n); G[v + n].pb(u + n);
	}
	int q; scanf("%d", &q);
	while (q--) {
		int k, x;
		scanf("%d%d", &k, &x);
		limt[k] = x;
	}
	scanf("%d", &q);
	while (q--) {
		int k, x;
		scanf("%d%d", &k, &x);
		limt[k + n] = x;
	}
	flag = 1;
	dfs(x, 0, x); dfs(y + n, 0, y + n);

	int S = 0, T = 2 * n + 1;
	F.init(2 * n + 2);
	int sum1 = 0, sum2 = 0;
	for (int i = 1; i <= n; i++)
		if (limt[i]) F.edge_add(S, i, limt[i], 0), sum1 += limt[i];
	for (int i = 1; i <= n; i++)
		if (limt[i + n]) F.edge_add(i + n, T, limt[i + n], 0), sum2 += limt[i + n];
	for (int i = 1; i <= n; i++)
		F.edge_add(pre[i], pre[i + n], 1, -val[i]);
	int flow;
	int ans = -F.zkw(S, T, flow);
	if (sum1 != sum2 || flow != sum1) flag = 0;
	if (!flag) puts("-1");
	else printf("%d\n", ans);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章