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 }
posted @ 2018-12-28 22:44  阿波罗2003  阅读(306)  评论(0编辑  收藏  举报