Codeforces Gym 101190M Mole Tunnels - 费用流
题目传送门
题目大意
$m$只鼹鼠有$n$个巢穴,$n - 1$条长度为$1$的通道将它们连通且第$i(i > 1)$个巢穴与第$\left\lfloor \frac{i}{2}\right\rfloor$个巢穴连通。第$i$个巢穴在最终时允许$c_i$只醒来的鼹鼠最终停留在这。已知第$i$只鼹鼠在第$p_i$个巢穴睡觉。要求求出对于每个满足$1 \leqslant k \leqslant n$的$k$,如果前$k$只鼹鼠醒来,最小的移动距离的总和。
考虑费用流的建图和暴力做法,把原图的边容量设为无限大,费用设为1(每条无向边要拆成两条),每个点再向汇点连一条边,容量为$c_i$,费用为0。
对于每次源点向$p_i$增广1单位的流量。
显然这样会超时,考虑优化费用流。
显然有:
- 每次增广的路径一定是一条简单路径
- 对于原图的一条无向边,它两个方向的边不会同时有流量(走另外一条弧的反向弧显然可以减少费用)
那么每次枚举路径的LCA,对于每个点记录一下$f_i$表示从$i$走到子树内的任意一个点的最短距离。
显然每次更新边权后很容易能够维护$f$。时间复杂度$O(n\log n)$。
Code
1 /** 2 * Codeforces 3 * Gym#101190M 4 * Accepted 5 * Time: 108ms 6 * Memory: 2200k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #ifndef WIN32 12 #define Auto "%lld" 13 #else 14 #define Auto "%I64d" 15 #endif 16 using namespace std; 17 typedef bool boolean; 18 19 #define ll long long 20 21 const signed ll llf = (signed ll) (~0ull >> 3); 22 const signed int inf = (signed) (~0u >> 2); 23 const int N = 131072; 24 25 #define pii pair<int, int> 26 27 pii operator + (pii a, int b) { 28 return pii(a.first + b, a.second); 29 } 30 31 int n, m; 32 int w[N], c[N]; 33 pii fd[N]; 34 35 inline void init() { 36 scanf("%d%d", &n, &m); 37 for (int i = 1; i <= n; i++) { 38 scanf("%d", c + i); 39 } 40 } 41 42 int value(int p, int dir) { // up : +1, down : -1 43 int prod = w[p] * dir; 44 return (prod >= 0) ? (1) : (-1); 45 } 46 47 void __update(int p) { 48 fd[p] = pii(inf * (!c[p]), p); 49 if ((p << 1) <= n && fd[p << 1].first != inf) 50 fd[p] = min(fd[p], fd[p << 1] + value(p << 1, -1)); 51 if ((p << 1) < n && fd[p << 1 | 1].first != inf) 52 fd[p] = min(fd[p], fd[p << 1 | 1] + value(p << 1 | 1, -1)); 53 } 54 55 int update(int s) { 56 int val = fd[s].first, g = s, v = fd[s].second, len = 0; 57 for (int p = s, d = (p & 1), q, cmp; len += value(p, 1), p >>= 1; d = p & 1) { 58 q = p << 1 | (d ^ 1); 59 if (q <= n && (cmp = fd[q].first + value(q, -1) + len) < val) 60 val = cmp, g = p, v = fd[q].second; 61 if (c[p] && len < val) 62 val = len, g = v = p; 63 } 64 c[v]--; 65 for (int p = s; p != g; p >>= 1) { 66 w[p]++; 67 __update(p); 68 } 69 for (int p = v; p != g; p >>= 1) { 70 w[p]--; 71 __update(p); 72 } 73 for (int p = g; p; p >>= 1) 74 __update(p); 75 // cerr << s << " " << v << '\n'; 76 return val; 77 } 78 79 inline void solve() { 80 for (int i = 1; i <= n; i++) 81 fd[i] = pii(inf * (!c[i]), i); 82 for (int i = n; i > 1; i--) 83 fd[i >> 1] = min(fd[i >> 1], fd[i] + 1); 84 85 int x; 86 ll res = 0; 87 while (m--) { 88 scanf("%d", &x); 89 res += update(x); 90 printf(Auto" ", res); 91 } 92 } 93 94 int main() { 95 freopen("mole.in", "r", stdin); 96 freopen("mole.out", "w", stdout); 97 init(); 98 solve(); 99 return 0; 100 }