[NowCoder5666H]Minimum-cost Flow

本文深入探讨了最小费用流算法的应用,特别是在解决特定类型的网络流问题中,如何通过调整边的容量来计算从源点到汇点的最小费用。文章详细介绍了算法流程,并给出了一种高效的实现方式,同时提供了代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

nnn个点,mmm条边的图,边有费用。

qqq次询问:若每条边的容量为uv\displaystyle\frac uvvu,则从111nnn111个单位的流量的最小费用是多少,要求输出最简分数。

n≤50,m≤100,q≤105,0≤u≤v≤109n\le50,m\le100,q\le10^5,0\le u\le v\le10^9n50,m100,q105,0uv109


题解

首先设cost(x,y){\rm cost}(x,y)cost(x,y)表示每条边容量为yyy,要从111nnnxxx的流量的费用。则有cost(x,y)=ycost(xy,1)\displaystyle{\rm cost}(x,y)=y{\rm cost}(\frac xy,1)cost(x,y)=ycost(yx,1)

根据费用流的算法流程:cost(ny+y,y)−cost(ny,y){\rm cost}(ny+y,y)-{\rm cost}(ny,y)cost(ny+y,y)cost(ny,y)表示每条边容量为yyy时,第n+1n+1n+1条增广路的费用(流量为y)。若只需要zzz的流量,则乘上比例系数zy\displaystyle\frac zyyz即可。

对于cost(1,p){\rm cost}(1,p)cost(1,p),设k=⌊1p⌋\displaystyle k=\lfloor\frac1p\rfloork=p1。考虑对前kkk条增广路每条路流ppp的流量,费用为cost(kp,p)=pcost(k,1){\rm cost}(kp,p)=p{\rm cost}(k,1)cost(kp,p)=pcost(k,1);对第k+1k+1k+1条增广路流1−kp1-kp1kp的流量,费用为1−kpp[cost(kp+p,p)−cost(kp,p)]=(1−kp)[cost(k+1,1)−cost(k,1)]\displaystyle\frac{1-kp}{p}[{\rm cost}(kp+p,p)-{\rm cost}(kp,p)]=(1-kp)[{\rm cost}(k+1,1)-{\rm cost}(k,1)]p1kp[cost(kp+p,p)cost(kp,p)]=(1kp)[cost(k+1,1)cost(k,1)]

fk=cost(k,1)f_k={\rm cost}(k,1)fk=cost(k,1),则有
cost(1,p)=pfk+(1−pk)(fk+1−fk){\rm cost}(1,p)=pf_k+(1-pk)(f_{k+1}-f_k)cost(1,p)=pfk+(1pk)(fk+1fk)

综上,设所有边容量为111,做一次最小费用最大流求出所有的fkf_kfk。对于一次询问p=uv\displaystyle p=\frac uvp=vu,则答案为ufk+(v−ku)(fk+1−fk)v\displaystyle\frac{uf_k+(v-ku)(f_{k+1}-f_k)}{v}vufk+(vku)(fk+1fk),化成最简分数即可。

单组数据时间复杂度O(n2m+qlog⁡V)O(n^2m+q\log V)O(n2m+qlogV)

#include <bits/stdc++.h>
using namespace std;
const int N = 50 + 5, M = 2 * 1000 + 5;
const long long Inf = 1e17;
using ll = long long;
using arr = int[N];
/*-----------------------------------------------------------------*/
int n, m, q, Ecnt, Fi[N];
ll dis[N], f[N];
struct Edge {
    int nx, v, c, w;
} E[M];
inline void Add(int u, int v, int c, int w) {
    E[++Ecnt] = (Edge){Fi[u], v, c, w}, Fi[u] = Ecnt;
}
class MCMF {
  private:
    static const int N = 2e5 + 5;
    ll h[N];
    int Flow[N], Pre[N], From[N];
    struct Seg {
        int Cnt, tr[N << 2];
        inline void Cls() {
            fill(tr, tr + 2 * Cnt + 1, 0);
            Cnt = 0;
        }
        inline int Cmp(int a, int b) { return dis[a] < dis[b] ? a : b; }
        inline void Set(int n) {
            for (Cnt = 1; Cnt < n + 2; Cnt <<= 1)
                ;
            tr[0] = n + 1;
        }
        inline void Mdy(int u, ll w) {
            for (int i = u + Cnt; dis[tr[i]] > w; tr[i] = u, i >>= 1)
                ;
            dis[u] = w;
        }
        inline void Del(int u) {
            for (tr[u += Cnt] = 0, u >>= 1; u;
                 tr[u] = Cmp(tr[u << 1], tr[u << 1 | 1]), u >>= 1)
                ;
        }
    } zkw;
    inline bool Dij(int n, int S, int T) {
        for (int i = 0; i <= n; ++i)
            dis[i] = Inf, Flow[i] = From[i] = Pre[i] = 0;
        Flow[S] = 1e9;
        dis[n + 1] = 0;
        zkw.Cls(), zkw.Set(n);
        zkw.Mdy(S, 0);
        for (int _ = 2; _ <= n; ++_) {
            int u = zkw.tr[1];
            zkw.Del(u);
            for (int i = Fi[u], v = E[i].v; i; v = E[i = E[i].nx].v)
                if (E[i].c > 0 && dis[v] > dis[u] + E[i].w + h[u] - h[v]) {
                    zkw.Mdy(v, dis[u] + E[i].w + h[u] - h[v]);
                    From[v] = u, Pre[v] = i;
                    Flow[v] = min(Flow[u], E[i].c);
                }
        }
        return dis[T] != Inf;
    }

  public:
    int MF;
    inline void Calc(int n, int S, int T) {
        int Now;
        for (int i = 1; i <= n; ++i)
            h[i] = 0;
        while (Dij(n, S, T)) {
            MF += (Now = Flow[T]);
            f[MF] = f[MF - Now] + Now * (dis[T] - h[S] + h[T]);
            for (int v = T; v != S; v = From[v])
                E[Pre[v]].c -= Now, E[Pre[v] ^ 1].c += Now;
            for (int i = 1; i <= n; ++i)
                h[i] += dis[i];
        }
    }
} D;
inline void CLS() {
    Ecnt = 1;
    D.MF = 0;
    for (int i = 1; i <= n; ++i)
        Fi[i] = f[i] = 0;
}
inline void Solve() {
    CLS();
    for (int u, v, w; m--;) {
        scanf("%d%d%d", &u, &v, &w);
        Add(u, v, 1, w), Add(v, u, 0, -w);
    }
    D.Calc(n, 1, n);
    scanf("%d", &q);
    for (ll u, v, k; q--;) {
        scanf("%lld%lld", &u, &v);
        if (u == 0 || (k = v / u) > D.MF || (v - u * k > 0 && !f[k + 1]))
            puts("NaN");
        else {
            ll a = (v - k * u) * (f[k + 1] - f[k]) + u * f[k], b = v,
               g = __gcd(a, b);
            printf("%lld/%lld\n", a / g, b / g);
        }
    }
}
int main() {
    while (~scanf("%d%d", &n, &m))
        Solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值