• Codeforces Gym 101190M Mole Tunnels


    题目传送门

      传送门

    题目大意

      $m$只鼹鼠有$n$个巢穴,$n - 1$条长度为$1$的通道将它们连通且第$i(i > 1)$个巢穴与第$leftlfloor frac{i}{2} ight floor$个巢穴连通。第$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(nlog 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 << '
    ';
     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 }
  • 相关阅读:
    单表清除重复数据
    调用webApi封装
    简单写入本地日志,日志文件位置与主程序exe位置相同
    APPConfig.XML获取配置文件(主程序和Dll各自的)
    获取当前运行程序上一级目录指定文件夹,没有就创建文件夹
    shell脚本中的单引号和双引号以及反引号详解
    Linux shell中反引号(`)的应用
    关于网页 硬解 软解 H264 HEVC 和你电脑起飞了那点事
    浏览器支持H.265解码总结
    微软、谷歌、亚马逊、Facebook等硅谷大厂91个开源软件盘点(附下载地址)
  • 原文地址:https://www.cnblogs.com/yyf0309/p/10193274.html
Copyright © 2020-2023  润新知