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;
}

 

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