大致题意
给出一个费用流图,每条边的流量上限相同且不固定。
有个询问,每个询问中给出每条边的流量上限(分数,且保证)。当图中的流量为 个单位的时候,求出此时的费用。
分析
首先是询问个数,有次询问,则需要预处理整个图,然后O(1)作答才可以过
然后注意到题目中给出的数据规模,图的边数只有条
首先由于边的流量均为分数(),而总流量为 个单位。我们先扩大倍,将每条边的流量固定为 个单位,此时流量为 个单位
考虑在最大流使用 SPFA 查找路径时,当查找到一条路径时,此路径的流量一定为 (根据上述的设定)。由于采用的本身便是最短路查找路径,得到的路径的费用为当前网络图下的最低费用。
所以可以稍微修改费用流板子中的一部分代码,使得每次得到路径并结算费用时,将每次得到的路径费用记录下来并保存。
#define ll long long const int maxn = 100; //点数 const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, flow, cost; Edge(int u, int v, int c, int f, int cc) : from(u), to(v), cap(c), flow(f), cost(cc) {} }; vector<ll> res; struct MCMF { int n, m; vector<Edge> edges; vector<int> G[maxn]; int inq[maxn]; //是否在队列中 int d[maxn]; //bellmanford int p[maxn]; //上一条弧 int a[maxn]; //可改进量 void init(int n) { this->n = n; for (int i = 0; i <= n; ++i) G[i].clear(); edges.clear(); } void addEdge(int from, int to, int cap, int cost) { edges.emplace_back(Edge(from, to, cap, 0, cost)); edges.emplace_back(Edge(to, from, 0, 0, -cost)); m = int(edges.size()); G[from].emplace_back(m - 2); G[to].emplace_back(m - 1); } bool spfa(int s, int t, int &flow, ll &cost) { for (int i = 1; i <= n; ++i) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; queue<int> q; a[s] = INF; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0; for (int i = 0; i < int(G[u].size()); ++i) { Edge &e = edges[G[u][i]]; if (e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if (!inq[e.to]) { q.push(e.to); inq[e.to] = 1; } } } } if (d[t] == INF) return false; flow += a[t]; cost += (ll) d[t] * (ll) a[t]; res.push_back(d[t]); for (int u = t; u != s; u = edges[p[u]].from) { edges[p[u]].flow += a[t]; edges[p[u] ^ 1].flow -= a[t]; } return true; } int MincostMaxflow(int s, int t, ll &cost) { int flow = 0; cost = 0; while (spfa(s, t, flow, cost)); return flow; } } mcmf;
通过 res 数组记录下所有得到的路径的费用
而后分解流量。对于每一次询问,从 res 数组中从开始取值每次取出一条路径来提供 个单位的流量,直到满足流量要求,则停止取值并输出答案。
虽然 ,可能需要循环遍历 次才能出结果,但是由于图中每条边的容量都是 ,所以对于每一条路径,它只有被完全占用和完全空闲两种状态,而边数只有 条,即整个网络的最大流量只能是 个单位,即最多遍历 次,则复杂度不超过 ,当 时,则无解,输出
注意一下乘法运算只需要考虑分子部分,分母部分不需要参与运算,在最后需要分子分母都除以 以保证最简
AC code
#include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = 100; //点数 const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, flow, cost; Edge(int u, int v, int c, int f, int cc) : from(u), to(v), cap(c), flow(f), cost(cc) {} }; vector<long long> res; struct MCMF { int n, m; vector<Edge> edges; vector<int> G[maxn]; int inq[maxn]; //是否在队列中 int d[maxn]; //bellmanford int p[maxn]; //上一条弧 int a[maxn]; //可改进量 void init(int n) { this->n = n; for (int i = 0; i <= n; ++i) G[i].clear(); edges.clear(); } void addEdge(int from, int to, int cap, int cost) { edges.emplace_back(Edge(from, to, cap, 0, cost)); edges.emplace_back(Edge(to, from, 0, 0, -cost)); m = int(edges.size()); G[from].emplace_back(m - 2); G[to].emplace_back(m - 1); } bool spfa(int s, int t, int &flow, ll &cost) { for (int i = 1; i <= n; ++i) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; queue<int> q; a[s] = INF; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0; for (int i = 0; i < int(G[u].size()); ++i) { Edge &e = edges[G[u][i]]; if (e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if (!inq[e.to]) { q.push(e.to); inq[e.to] = 1; } } } } if (d[t] == INF) return false; flow += a[t]; cost += (ll) d[t] * (ll) a[t]; res.push_back(d[t]); for (int u = t; u != s; u = edges[p[u]].from) { edges[p[u]].flow += a[t]; edges[p[u] ^ 1].flow -= a[t]; } return true; } int MincostMaxflow(int s, int t, ll &cost) { int flow = 0; cost = 0; while (spfa(s, t, flow, cost)); return flow; } } mcmf; int n, m; void solve() { ios::sync_with_stdio(false); cin.tie(0); cout.precision(10); cout << fixed; while (cin >> n >> m) { mcmf.init(n + 10); res.clear(); for (int i = 0; i < m; ++i) { int u, v, f; cin >> u >> v >> f; mcmf.addEdge(u, v, 1, f); } ll cost = 0; ll mf = mcmf.MincostMaxflow(1, n, cost); int q; cin >> q; while (q--) { ll u, v; cin >> u >> v; if (mf * u < v) { cout << "NaN" << '\n'; continue; } ll sum = 0; ll ans = 0; for (auto item : res) { if (sum + u <= v) { sum += u; ans += item * u; } else { ans += (v - sum) * item; break; } } ll g = __gcd(ans, v); cout << ans / g << '/' << v / g << '\n'; } } } signed main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); #ifdef ACM_LOCAL freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); int test_index_for_debug = 1; char acm_local_for_debug; while (cin >> acm_local_for_debug) { if (acm_local_for_debug == '$') exit(0); cin.putback(acm_local_for_debug); if (test_index_for_debug > 20) { throw runtime_error("Check the stdin!!!"); } auto start_clock_for_debug = clock(); solve(); auto end_clock_for_debug = clock(); cout << "Test " << test_index_for_debug << " successful" << endl; cerr << "Test " << test_index_for_debug++ << " Run Time: " << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl; cout << "--------------------------------------------------" << endl; } #else solve(); #endif return 0; }