• Solution 「ZJOI 2010」「洛谷 P2570」贪吃的老鼠


    \(\mathscr{Description}\)

      Link.

      有 \(n\) 块奶酪,每块奶酪出现时段为 \([s_i,t_i]\),体积为 \(V_i\);有 \(m\) 只老鼠要吃奶酪,第 \(i\) 只每秒吃 \(v_i\) 体积的奶酪。一块奶酪不能同时被多于一只老鼠吃,一只老鼠不能同时吃多于一块奶酪。求最小的 \(T\),使得所有 \(t_i\leftarrow t_i+T\) 后,老鼠可以把奶酪吃干净。

      多测,数组组数 \(K\le 5\)\(n,m\le30\)\(1\le v_i,V_i\le10^5\),所有给定数据为整数,注意老鼠不一定需要在整数时刻改变进食状态。

    \(\mathscr{Solution}\)

      神妙网络流,给出了一个拆解“不能一对多”限制的建图模型。

      显然可以二分 \(T\),继而离散化出 \(\mathcal O(n)\) 个奶酪存在状态没有改变的时段。对于跨度为 \(\Delta t\) 的时段,老鼠 \(i\) 的进食量最多是 \(v_i\Delta t\),但问题是,如果直接在奶酪-老鼠的二分图上两两连边,完全无法刻画“一块奶酪不能同时被多于一只老鼠吃,一只老鼠不能同时吃多于一块奶酪”这一限制,这也是本题的难点。

      注意,网络流算法本身不可能去描述“只能一对一”,我们需要从建图修改,达到一种《奶酪“看上去”在被很多只老鼠吃,实际上等价于在被一只老鼠吃》的效果。怎么办?考虑差分。将老鼠按 \(v\) 升序排列,奶酪 \(i\) 连向老鼠 \(j\),容量为 \((v_j-v_{j-1})\Delta t\),表明我们可以把吃奶酪 \(i\) 的老鼠的速度从 \(v_{j-1}\) 提升到 \(v_j\),并保持一段时间,这段时间的长度作为流量,在网络流中将进行最优化抉择。另一方面,每一种“升级”的可用次数是不同的:从 \(v_{j-1}\) 升级到 \(v_j\) 的总时间自然不超过 \((m-j+1)\Delta t\)。从规范化的角度,如果一块奶酪选择了一些不连续的升级,考虑到可用升级时间的单降性,一定能够通过调整使之连续,使之能够对应到实际问题的方案上去,正确性就得以保证了。

      复杂度 \(\mathcal O(K\log V\operatorname{Dinic}(nm,n^2m))\)

    \(\mathscr{Code}\)

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
    #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
    
    const double EPS = 1e-7;
    const int MAXN = 30, IINF = 0x3f3f3f3f;
    int n, m, vol[MAXN + 5], st[MAXN + 5], ed[MAXN + 5], spd[MAXN + 5];
    
    inline double dabs(const double x) { return x < 0 ? -x : x; }
    inline int sign(const double x) { return dabs(x) <= EPS ? 0 : x < 0 ? -1 : 1; }
    
    namespace FG {
    
    const int MAXND = 2 * MAXN * MAXN + MAXN + 2;
    const int MAXEG = MAXN + 2 * MAXN * MAXN + 2 * MAXN * MAXN * MAXN;
    int S, T, B, ecnt = 1, head[MAXND + 5], dis[MAXND + 5], curh[MAXND + 5];
    struct Edge { int to; double flw; int nxt; } graph[MAXEG * 2 + 5];
    
    inline void clear() {
        ecnt = 1;
        rep (i, 0, B) head[i] = 0;
        B = -1;
    }
    
    inline void link(const int s, const int t, const double w) {
        // printf("%d %d %d\n", s, t, w);
        graph[++ecnt] = { t, w, head[s] }, head[s] = ecnt;
        graph[++ecnt] = { s, 0, head[t] }, head[t] = ecnt;
    }
    
    inline bool bfs() {
        static int que[MAXND + 5], hd, tl;
        rep (i, 0, B) dis[i] = IINF;
        dis[que[hd = tl = 1] = S] = 0;
        while (hd <= tl) {
            int u = que[hd++];
            for (int i = head[u], v; i; i = graph[i].nxt) {
                if (sign(graph[i].flw) && dis[v = graph[i].to] > dis[u] + 1) {
                    dis[que[++tl] = v] = dis[u] + 1;
                }
            }
        }
        return dis[T] != IINF;
    }
    
    inline double augment(const int u, double iflw) {
        if (u == T) return iflw;
        double ret = 0.;
        for (int &i = curh[u], v; i; i = graph[i].nxt) {
            if (sign(graph[i].flw) && dis[v = graph[i].to] == dis[u] + 1) {
                double t = augment(v, std::min(iflw, graph[i].flw));
                graph[i].flw -= t, graph[i ^ 1].flw += t, iflw -= t, ret += t;
                if (!sign(iflw)) break;
            }
        }
        if (!sign(ret)) dis[u] = IINF;
        return ret;
    }
    
    inline double dinic() {
        double ret = 0;
        while (bfs()) {
            rep (i, 0, B) curh[i] = head[i];
            ret += augment(S, IINF);
        }
        return ret;
    }
    
    } // namespace FG.
    
    inline bool check(const double x) {
        static double peri[MAXN * 2 + 5];
        int pcnt = 0, req = 0;
        rep (i, 1, n) peri[++pcnt] = st[i], peri[++pcnt] = ed[i] + x;
        std::sort(peri + 1, peri + pcnt + 1);
    
        FG::clear(), FG::S = ++FG::B;
        rep (i, 1, n) FG::link(FG::S, ++FG::B, vol[i]), req += vol[i];
        FG::T = ++FG::B;
        rep (i, 2, pcnt) if (sign(peri[i] - peri[i - 1])) {
            double dur = peri[i] - peri[i - 1];
            rep (j, 1, m) {
                FG::link(++FG::B, FG::T, dur * spd[j] * (m - j + 1.));
                rep (k, 1, n) {
                    if (sign(st[k] - peri[i - 1]) <= 0
                      && sign(ed[k] + x - peri[i]) >= 0) {
                        FG::link(k, FG::B, dur * spd[j]);
                    }
                }
            }
        }
        return !sign(FG::dinic() - req);
    }
    
    int main() {
        int T; scanf("%d", &T);
        while (T--) {
            scanf("%d %d", &n, &m);
            rep (i, 1, n) scanf("%d %d %d", &vol[i], &st[i], &ed[i]);
            rep (i, 1, m) scanf("%d", &spd[i]);
            std::sort(spd + 1, spd + m + 1);
            per (i, m, 1) spd[i] -= spd[i - 1];
    
            double l = 0., r = 3e6;
            while (l + EPS < r) {
                double mid = 0.5 * (l + r);
                if (check(mid)) r = mid;
                else l = mid;
            }
            printf("%f\n", l);
        }
        return 0;
    }
    
    
  • 相关阅读:
    Git:常用命令记录
    JS笔记(二):隐式转换
    vertical-align/line-height:水平垂直居中
    JS笔记(一):声明提升
    Array.prototype.sort():从一道面试题说起
    CSS笔记(一):选择器规范
    FreeCodeCamp:Profile Lookup
    tile_images_offset的简单使用
    vs2013快捷键等(转)
    Qt状态栏的使用(转)
  • 原文地址:https://www.cnblogs.com/rainybunny/p/16009946.html
Copyright © 2020-2023  润新知