只要你跑的够快,锅就追不上你

0%

「NEERC 2016」Mole Tunnels(费用流)

题目大意

「NEERC 2016」Mole Tunnels(BZOJ 4849)

有 $n$ 个洞,$m$ 只鼠,第 $i$ 个洞里有 $c_i$ 份食物。如果 $i > 1$,那么第 $i$ 个洞向 $\lfloor \frac{i}{2} \rfloor$ 个洞连一条无向边。对于每个 $k$,你要为前 $k$ 只鼠制定一个移动方案,使得它们各自移动到某个洞穴后,每个洞穴的鼠的数量不超过该洞中食物的数量(只算前 $k$ 只鼠)。在合法的情况下,要求鼠移动的距离总和最小,你只需输出最小的距离和。

数据范围:$n, m \le 10^5$。

思路分析

首先想到一个费用流的做法:可以将老鼠到某个洞里看作一个匹配,代价为它们在树上的距离。具体地:

  • 从 $S$ 向 $i$ 连边,容量为 $i$ 号洞中的老鼠个数,花费为 $0$。
  • 从 $i$ 向 $T$ 连边,容量为 $c_i$,花费为 $0$。
  • 对于树上的边 $(u, v)$,从 $u$ 向 $v$ 连一条容量为 $+ \infty$,花费为 $1$ 的边,并从 $v$ 向 $u$ 也连一条容量,花费相同的边。

发现这样复杂度太高,不能通过。考虑模拟费用流的过程。对于每个点,我们记录它和它父亲在做费用流时边的状态(需要记录反向边),并维护它的子树内部最近的有食物的点。每次加入一只鼠的时候,我们暴力地向上跳,找到一个最近的点。之后,将它到这个点的路径增广,同时修改边的花费即可,具体过程可以看代码。由于保证树高 $\log n$,所以总时间复杂度为 $O(n \log n)$。

代码实现

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 1e5, inf = 1e9 + 1;
5
int n, m, ans, c[maxn + 3], w[maxn + 3][2], p[maxn + 3], dp[maxn + 3];
6
7
void dfs(int u) {
8
	if (c[u]) {
9
		dp[u] = 0, p[u] = u;
10
	} else {
11
		dp[u] = inf, p[u] = 0;
12
	}
13
	for (int i = 0, v; i < 2; i++) {
14
		v = u * 2 + i;
15
		if (v <= n) {
16
			dfs(v);
17
			if (p[v] && dp[v] + 1 < dp[u]) {
18
				dp[u] = dp[v] + 1, p[u] = p[v];
19
			}
20
		}
21
	}
22
}
23
24
void update(int u) {
25
	if (!u) {
26
		return;
27
	}
28
	if (c[u]) {
29
		dp[u] = 0, p[u] = u;
30
	} else {
31
		dp[u] = inf, p[u] = 0;
32
	}
33
	for (int i = 0, v; i < 2; i++) {
34
		v = u * 2 + i;
35
		if (v <= n) {
36
			if (p[v] && dp[v] + (w[v][0] ? -1 : 1) < dp[u]) {
37
				dp[u] = dp[v] + (w[v][0] ? -1 : 1), p[u] = p[v];
38
			}
39
		}
40
	}
41
	update(u >> 1);
42
}
43
44
int insert(int u) {
45
	int res = inf, pnt = 0, lca = 0;
46
	for (int i = u, j = 0; i; j += (w[i][1] ? -1 : 1), i >>= 1) {
47
		if (p[i] && dp[i] + j < res) {
48
			res = dp[i] + j, pnt = p[i], lca = i;
49
		}
50
	}
51
	for (int i = u; i != lca; i >>= 1) {
52
		w[i][1] ? w[i][1]-- : w[i][0]++;
53
	}
54
	for (int i = pnt; i != lca; i >>= 1) {
55
		w[i][0] ? w[i][0]-- : w[i][1]++;
56
	}
57
	c[pnt]--;
58
	update(u);
59
	update(pnt);
60
	return res;
61
}
62
63
int main() {
64
	scanf("%d %d", &n, &m);
65
	for (int i = 1; i <= n; i++) {
66
		scanf("%d", &c[i]);
67
	}
68
	dfs(1);
69
	for (int i = 1, t; i <= m; i++) {
70
		scanf("%d", &t);
71
		ans += insert(t);
72
		printf("%d%c", ans, " \n"[i == m]);
73
	}
74
	return 0;
75
}