jzoj6478 C (上下界最小费用流)

题意

给一棵树和树上的若干条祖孙路径。

  1. 现在可以给路径染红色或蓝色,各有一个代价。
  2. 覆盖每条边的红色和蓝色路径各有一个上限。

现求最小染色代价。
n ≤ 150 n\leq150 n150

分析

  • 基本可以判断是个网络流
  • 构造如下:
  • 先默认所有路径染色为蓝,再将调整成红色这个选项构造出来。
  • 对树上点x,连边 ( x , f a [ x ] , [ . . . ] , 0 ) (x,fa[x],[...],0) (x,fa[x],[...],0),这个上下界算一下就知道。
  • 路径(s, t)(s是祖先),连边 ( S , t , [ 1 , 1 ] , 0 ) , ( s , T , [ 1 , 1 ] , 0 ) , ( t , s , [ 0 , 1 ] , c o s t r − c o s t b ) (S,t,[1,1],0),(s,T,[1,1],0),(t,s,[0,1],costr-costb) (S,t,[1,1],0),(s,T,[1,1],0),(t,s,[0,1],costrcostb)
  • 走第三条边表示该边切换为红。走树边表示该边被染了一次蓝色。
  • 经验告诉我这样构图会出现一个问题。就是当流量合到一起之后,是无法区分出来的。
  • 然而这题并不需要区分出来。我们的限制只是每条边的流量,这个只由子树内的净流量决定。
  • 换而言之,对于每一种可行的方案来说,你可以验证,每条树边被经过的次数是正确的。无论这些流量的来源是不是真正覆盖这条边的那些路径。
  • 上下界最小流的思想是:预先流下界,然后每个点会有一个盈亏。亏点(需要流出来支持下界)向超级汇tt连边,超级源ss向盈点连边。连(T,S,inf),再跑sstt最小费用最大流,这样跑出来每一流量都能对应原图中一流量。
  • 不满流则无解。这道题由于连边已经决定了下界即最大流,所以不需要再跑s,t最大流。
#include <bits/stdc++.h>
using namespace std;
const int N = 200, M = N * 20, inf = 1e9;
int n, m, S, T, tot = 1, ss, tt;
int to[M], nex[M], final[N], f[M], cost[M];
int out[N], ans;
int from[M], fa[N], limr[N], limb[N], vr[N], vb[N];
int cnt[N];
vector<int> son[N];
int nos;
void dfs(int x) {
	for(int y : son[x]) {
		dfs(y);
		cnt[x] += cnt[y];
	}
	ans += cnt[x] * vr[x];
}

void _link(int x, int y, int c, int _f) {
	to[++tot] = y, nex[tot] = final[x], final[x] = tot;
	cost[tot] = c, f[tot] = _f;
	from[tot] = x;
}

void link(int x, int y, int c, int l, int r) {
	if (l > r) {
		printf("-1\n"); exit(0);
	}
	ans += c * l;
	out[x] += l, out[y] -= l; r -= l;
	_link(x, y, c, r), _link(y, x, -c, 0);
}

namespace sap {
	int dis[N], upd[N];
	void spfa() {
		static int Q[N * 10], vis[N];
		int h = 0, t = 0; Q[++t] = ss;
		memset(dis, 127, sizeof dis); dis[ss] = 0;
		memset(vis, 0, sizeof vis);
		while (h < t) {
			int x = Q[++h];
			for(int i = final[x]; i; i = nex[i]) if (f[i]) {
				int y = to[i];
				if (dis[x] + cost[i] < dis[y]) {
					if (!vis[y]) Q[++t] = y;
					dis[y] = dis[x] + cost[i];
					upd[y] = i;
				}
			}
		}
	}
	int maxflow() {
		int ret = 0;
		while (spfa(), dis[tt] != dis[0]) {
			int fw = inf;
			for(int x = upd[tt]; x; x = upd[from[x]]) {
				fw = min(fw, f[x]);
			}
			for(int x = upd[tt]; x; x = upd[from[x]]) {
				f[x] -= fw;
				f[x ^ 1] += fw;
				ans += fw * cost[x];
			}
			ret += fw;
		}
		return ret;
	}
	void UDB_mincostflow() {
		_link(T, S, 0, inf);
		_link(S, T, 0, 0);
		int ful = 0;
		for(int i = 1; i <= T; i++) {
			if (out[i] < 0) {
				ful += -out[i];
				_link(ss, i, 0, -out[i]);
				_link(i, ss, 0, 0);
			} else if (out[i] > 0) {
				_link(i, tt, 0, out[i]);
				_link(tt, i, 0, 0);
			}
		}
		if (maxflow() != ful) printf("-1");
		else cout << ans << endl;
	}
}

int main() {
	freopen("C.in", "r", stdin);
	// freopen("C.out", "w", stdout);
	cin >> n >> m;
	for(int i = 2; i <= n; i++) {
		scanf("%d %d %d %d %d", &fa[i], &limr[i], &limb[i], &vr[i], &vb[i]);
		son[fa[i]].push_back(i);
	}
	S = n + 1, T = S + 1, ss = T + 1, tt = ss + 1;
	for(int i = 1; i <= m; i++) {
		int s, t, costr, costb;
		scanf("%d %d %d %d", &s, &t, &costr, &costb);
		ans += costb;
		cnt[t]++, cnt[s]--;
		link(t, s, costr - costb, 0, 1);
		link(s, T, 0, 1, 1);
		link(S, t, 0, 1, 1);
	}
	dfs(1);
	for(int x = 2; x <= n; x++) {
		link(x, fa[x], vb[x] - vr[x], cnt[x] - limr[x], limb[x]);
	}
	sap :: UDB_mincostflow();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值