此题是以《NOI2008志愿者招募》为背景的,预做此题需要先体会《志愿者招募》的思想
只不过由线性结构变为树形结构,但是问题的本质没有变,都是一个元素影响连续的若干个位置,构图的本质都是使每个变量x出现分别以+和-的形式出现在两个恒等式中,由此可以看做x变量从+式流入-式流出。设药品为x,为每条河建立一个等式,则x出现在了u的父边到v的父边路径上的所有等式中,因此用每个点的父边减去所有孩子的边(设根节点的父边为全0,即没有限制)得到n个等式,且所有变量x、y均以+和-的形式各出现一次,由此可以构图。若求出最大流==源点的最大容量则最小费用即为解,否则无解。
《志愿者招募》题解写的不错,体会一下x、y变量的作用应该就会构图了,构图如下:
class node {
int be, ne;
int id, val;
node(int b, int e, int v) {
be = b;
ne = e;
val = v;
}
}
node buf[] = new node[maxn];
int E[] = new int[maxn], len;
void add(int a, int b, int v) {
buf[len] = new node(b, E[a], v);
id[b] = len;
E[a] = len++;
}
Scanner scan = new Scanner(System.in);
int n, m, id[] = new int[maxn], sum;
boolean is[] = new boolean[maxn];
MINCOST sp = new MINCOST();
void init() {
sp.init();
len = 0;
Arrays.fill(E, -1);
Arrays.fill(is, true);
sum = 0;
}
void run() {
int cas = scan.nextInt();
for (int k = 1; k <= cas; k++) {
System.out.print("Case #" + k + ": ");
n = scan.nextInt();
init();
int a, b, v;
for (int i = 1; i < n; i++) {
a = scan.nextInt();
b = scan.nextInt();
v = scan.nextInt();
is[a] = false;
add(b, a, v);
}
int root = -1;
for (int i = 1; i <= n; i++)
if (is[i]) {
root = i;
break;
}
id[root] = n - 1;
int s, t, l, c;
m = scan.nextInt();
for (int i = 0; i < m; i++) {
s = scan.nextInt();
t = scan.nextInt();
l = scan.nextInt();
c = scan.nextInt();
sp.addcap(id[s], id[t], l, c);
}
int temp = 0;
for (int i = E[root]; i != -1; i = buf[i].ne) {
temp += buf[i].val;
sp.addcap(n - 1, i, inf, 0);
dfs(i);
}
sp.addcap(n - 1, n + 1, temp, 0);
int ans = sp.solve(n, n + 1);
if (sp.maxflow == sum)
System.out.println(ans);
else
System.out.println(-1);
}
}
void dfs(int a) {
int temp = buf[a].val;
int v = buf[a].be;
for (int i = E[v]; i != -1; i = buf[i].ne) {
sp.addcap(a, i, inf, 0);
temp -= buf[i].val;
dfs(i);
}
if (temp > 0) {
sp.addcap(n, a, temp, 0);
sum += temp;
} else
sp.addcap(a, n + 1, -temp, 0);
}