H. Minimum-cost Flow(最小费用流)
Minimum-cost Flow
-
题意:给n个点,m条边的有向图,有q次询问,每次询问给两个数u, v。用来限制每一条边能使用的最大流量为u/v, 问把1单位从1点到n点的最小花费。(所有边容量相同)
-
题解:
- 我们可以设所有边费用为c/v,那输入流量和输出流量设为u/v跑一次Dinic就能得到所需最小花费。但是q<=1e5。这么做T飞了。所以最多跑一次Dinic。
- 为了方便处理,我们同时扩大费用和输入输出流量v倍。所以现在每条边容量为1,费用为c,输入输出流量为u。那我们可以这样想,每次spfa()会增加一条增广路,在跑Dinic时path[i]记录第i+1条增广路的费用。sum[i]表示满流时前i条增广路的总费用。
- 这样面对每次询问u,v。记录path[],sum[]。由于\(v = a * u +b\) .我们可以在所以增广路径中选择a条满流,一条路径不满流来构成v流量。由于增广路的选择过程本身就从小到大,所以贪心选前a条路径(即0 ~ a-1条增广路)来满流,选第a条增广路来贡献b流量。
- \(res = sum[a] * u + path[a] * b\)
-
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<map> #include<queue> #include<vector> #include<string> #include<fstream> using namespace std; #define rep(i, a, n) for(int i = a; i <= n; ++ i); #define per(i, a, n) for(int i = n; i >= a; -- i); typedef long long ll; const int N = 200100; const int mod = 998244353; const double Pi = acos(- 1.0); const ll INF = 1 << 30; const int G = 3, Gi = 332748118; ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; } ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } ll lcm(ll a, ll b) { return a * b / gcd(a, b);} bool cmp(int a, int b){ return a > b;} // ll dis[N]; int head[N], cnt; ll maxflow = 0, mincost = 0; struct node{ ll to, c, nxt, cost; }edge[N * 4]; int n, m, s, t, q; int vis[N]; vector<ll> path; ll sum[N], res = 0; void add(int u, int v, ll w, ll cost){ edge[cnt].to = v, edge[cnt].c = w, edge[cnt].cost = cost, edge[cnt].nxt = head[u], head[u] = cnt ++; edge[cnt].to = u, edge[cnt].c = 0, edge[cnt].cost =-cost, edge[cnt].nxt = head[v], head[v] = cnt ++; } bool spfa(){ for(int i = 0; i <= t + 1; ++ i){ vis[i] = 0; dis[i] = INF; } dis[s] = 0; vis[s] = 1; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i != -1; i = edge[i].nxt){ ll v = edge[i].to, w = edge[i].c, costt = edge[i].cost; if(w && dis[v] > dis[u] + costt){ dis[v] = dis[u] + costt; if(!vis[v]){ q.push(v); vis[v] = 1; } } } } if(dis[t] != INF) return 1; else return 0; } ll dfs(int u, ll flow){ if(u == t){ vis[t] = 1; maxflow += flow; return flow; } ll used = 0, rlow = 0; vis[u] = 1; for(int i = head[u]; i != -1; i = edge[i].nxt){ ll v = edge[i].to, w = edge[i].c, costt = edge[i].cost; if((!vis[v] || v == t) && w && dis[v] == dis[u] + costt){ if(rlow = dfs(v, min(flow - used, w))){ mincost += costt * rlow; edge[i].c -= rlow; edge[i ^ 1].c += rlow; used += rlow; if(used == flow) break; } } } return used; } ll mcmf(){ path.clear(); while(spfa()){ path.push_back(dis[t]); vis[t] = 1; while(vis[t]){ memset(vis, 0, sizeof(vis)); dfs(s, INF); } } return maxflow; } void init(){ memset(head, -1, sizeof(head)); maxflow = mincost = 0; cnt = 0; s = 1, t = n; } int main() { while(~scanf("%d%d",&n,&m)){ init(); while(m --){ int x, y; ll z; scanf("%d%d%lld",&x, &y, &z); add(x, y, 1ll, z); } mcmf(); int pnum = path.size(); sum[0] = 0; for(int i = 0; i < pnum; ++ i) sum[i + 1] = sum[i] + path[i]; scanf("%d",&q); while(q --){ ll u, v; scanf("%lld%lld",&u,&v); if(u * pnum < v){ printf("NaN\n"); continue; } ll a = v / u, b = v % u; res = u * sum[a] + path[a] * b; ll g = gcd(res, v); res /= g, v /=g; printf("%lld/%lld\n",res, v); } } return 0; }