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 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章