2020牛客暑期多校训练营(第一场)H-Minimum-cost Flow(最小费用流)

题目链接

题目大意:初始给定一个(n)个点,(m)条边,费用全部确定的网络。要求对于接下来的(k)次询问,每次都给定所有边的容量为一个分数(frac{u}{v})。要求对于每一个询问计算从点(1)到点(n)跑大小为(1)的流的最小花费。
大致思路:因为我们事先知道了所有边的费用,并且后续所有边的最大容量全部相同。那么初始建图的时候所有边的最大容量都设置为(1),这样我们只需要跑一边最小费用最大流,并且记录每一次的增广路径的花费(由于容量为(1),所以最少花费就是最短路的距离)。然后对于每一次询问我们进行分析:由于容量给定的是一个分数,所以我们先将所有边的容量扩大至(v)倍,那么所有边的最大容量就变成了(u)。所以现在我们要大小为(v)的流,最后得出的花费除以(v)就是最终所求的答案。由于所有的最大容量都为(u),所以每一次的增广所增加的流量都为(u)。最后一次把剩余的流量补全即可。如果最大流无法达到(v),就输出(NaN)。具体看代码实现。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class MinCostFlow {
public:
    struct Result {
        int flow, cost;
    };

    MinCostFlow(int n, int m = 0) : visited(n), head(n, -1), dist(n), prev(n) ,flow(n), preu(n){
        edges.reserve(m << 1);
    }

    void add_edge(int u, int v, int capacity, int cost) {
        internal_add_edge(u, v, capacity, cost);
        internal_add_edge(v, u, 0, -cost);
    }

    bool spfa(int src, int dst) {
        const int infdist = std::numeric_limits<int>::max();
        std::fill(dist.begin(), dist.end(), infdist);
        std::fill(flow.begin(), flow.end(), infdist);
        dist[src] = 0;
        std::queue<int> queue;
        queue.push(src);
        while (!queue.empty()) {
            int u = queue.front();
            queue.pop();
            visited[u] = false;
            for (int iter = head[u]; ~iter;) {
                int v = edges[iter].v;
                if (edges[iter].rest && dist[u] + edges[iter].cost < dist[v]) {
                    dist[v] = dist[u] + edges[iter].cost;//费用增广路的最短路
                    prev[v] = iter;//上一边
                    preu[v] = u;//前驱点
                    flow[v] = min(flow[u],edges[iter].rest);//增广的瓶颈流量
                    if (!visited[v]) {
                        visited[v] = true;
                        queue.push(v);
                    }
                }
                iter = edges[iter].next;
            }
        }
        return dist[dst]!=infdist;
    }
    vector<int> ans;
    Result MCMF(int src,int dst){
        int maxflow=0,mincost=0;
        while(spfa(src,dst)){
            int v=dst;
            ans.push_back(dist[dst]);
            maxflow+=flow[dst];
            mincost+=flow[dst]*dist[dst];
            while(v!=src){
                edges[prev[v]].rest-=flow[dst];
                edges[prev[v]^1].rest+=flow[dst];
                v=preu[v];
            }
        }
        return Result{maxflow,mincost};
    }

private:
    struct Edge {
        int v, next, rest, cost;//终点,下条边,容量,花费
    };

    void internal_add_edge(int u, int v, int capacity, int cost) {
        edges.push_back(Edge{v, head[u], capacity, cost});
        head[u] = edges.size() - 1;
    }

    std::vector<bool> visited;
    std::vector<int> head, dist, prev, flow, preu;
    std::vector<Edge> edges;
};

int main(){
    int n,m;
    while(cin>>n>>m){
        MinCostFlow net(n,m);
        for(int i=0,a,b,c;i<m;i++){
            cin>>a>>b>>c;
            net.add_edge(a-1,b-1,1,c);
        }
        auto res=net.MCMF(0,n-1);
        int q;
        cin>>q;
        while(q--){
            int u,v;
            scanf("%d%d",&u,&v);
            if(u==0)puts("NaN");
            else{
                ll fz=0,fm=v;
                for(int an : net.ans){
                    if(v>u)fz+=1ll*an*u,v-=u;
                    else {fz+=1ll*an*v,v=0;break;}
                }
                if(v)puts("NaN");
                else{
                    ll gd=__gcd(fz,fm);
                    printf("%lld/%lld
",fz/gd,fm/gd);
                }
            }
        }
    }
    return 0;
}