LOJ 6479 [ICPC World Finals 2017] 小小水管工 Son of Pipe Stream 题解
题意
- 给出
个城市和 条双向管道,以及两个实数 和 。有两种液体,分别是水和 Flubber(下面简写为 W 和 F)。 号和 号城市分别生产 Flubber 和水,并通过管道流入 号城市。对于一条管道,其中可以同时存在两种液体,但是方向必须相同。每条管道有一个容量 ,如果一条管道中有 个单位的水和 个单位的 Flubber,那么需要满足 。记最终流到 号城市的水和 Flubber 分别为 和 ,求 的最大值,并给出任意一个方案。 , , , 。
做法
首先
考虑如果我们不区分两种液体(即两个源点都可以产出两种液体),那么这就是一个普通最大流。设此时的流量为
现在考虑区分两种液体怎么做。
设两种液体的流量分别为
很容易发现这是错的。假设我们只保留 Flubber,设此时的最大流为
接下来就是人类智慧。感性理解一下我们能够发现,似乎对于任意一个
证明:
我们任取一个
的方案,记为 ;然后任取一个 的方案,记为 。
因为在中 ,而在 中 ,而我们知道 ,所以必然存在实数 使得 ,那么此时 就是流量为 的一个方案。
现在问题转化为,对于任意
求导过程:
记
,那么
因为
在定义域内连续,所以极值点即为所有 的点,即 三个点。代入原式发现 都是最小值,所以最大值就是 。
现在答案已经求出来了,但是还要构造方案。注意到我们一直没考虑水和 Flubber 的方向不能相反的限制,但其实这不影响,只要我们最终构造出来的方案满足这个限制就行了。
求方案其实也比较简单。首先我们给
于是我们建一个新的图,点还是原图的点,但是边的方向和流量为最优解中这条边的方向和流量。那么这个图其实本来就是一个最大流。然后我们 只 给
那么这道题就做完了,感觉难点在于这个结论和找方案。
代码
代码
#include <bits/stdc++.h>
const int N = 200 + 5;
const int M = N * N;
const int FLOW_N = N + 4;
const int FLOW_M = N * N * 2 + N + 2;
const double eps = 1e-8;
const double FINF = 1e18;
int n, m;
struct GraphEdge { int u, v; double w; } e[M];
double fe[M];
double V, A;
struct Dinic {
struct Edge { int to, nxt; double r; } edge[FLOW_M << 1];
int head[FLOW_N], cur[FLOW_N], ek;
int n, s, t;
int dep[FLOW_N];
std::queue<int> q;
void add_one_edge(int u, int v, double c) { edge[ek] = (Edge){v, head[u], c}, head[u] = ek++; }
bool bfs() {
for(int i = 1; i <= n; i++) dep[i] = 0;
dep[s] = 1, q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u]; i; i = edge[i].nxt) if(!dep[edge[i].to] && edge[i].r > eps) {
int v = edge[i].to;
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[t];
}
double dfs(int u, double in) {
if(u == t) return in;
double out = 0;
for(int &i = cur[u]; i; i = edge[i].nxt) if(dep[u] + 1 == dep[edge[i].to] && edge[i].r > eps) {
int v = edge[i].to;
double ret = dfs(v, std::min(in, edge[i].r));
if(ret < eps) continue;
edge[i].r -= ret, edge[i ^ 1].r += ret;
in -= ret, out += ret;
if(in < eps) return out;
}
if(out < eps) dep[u] = 0;
return out;
}
Dinic() : ek(2) {}
void init(int n_) { n = n_; ek = 2; for(int i = 1; i <= n; i++) head[i] = 0; }
void add_edge(int u, int v, double c) { add_one_edge(u, v, c), add_one_edge(v, u, 0); }
double maxflow(int s_, int t_) {
s = s_, t = t_;
double ret = 0;
while(bfs()) {
for(int i = 1; i <= n; i++) cur[i] = head[i];
ret += dfs(s, FINF);
}
return ret;
}
} dinic;
int main() {
scanf("%d%d%lf%lf", &n, &m, &V, &A);
for(int i = 1; i <= m; i++) scanf("%d%d%lf", &e[i].u, &e[i].v, &e[i].w);
int src = n + 1, dst = n + 2;
dinic.init(n + 2);
for(int i = 1; i <= m; i++) dinic.add_edge(e[i].u, e[i].v, e[i].w), dinic.add_edge(e[i].v, e[i].u, e[i].w);
dinic.add_edge(src, 2, FINF), dinic.add_edge(src, 1, FINF), dinic.add_edge(3, dst, FINF);
double flow = dinic.maxflow(src, dst);
dinic.init(n + 2);
for(int i = 1; i <= m; i++) dinic.add_edge(e[i].u, e[i].v, e[i].w), dinic.add_edge(e[i].v, e[i].u, e[i].w);
dinic.add_edge(src, 1, FINF), dinic.add_edge(3, dst, FINF);
double Fmax = dinic.maxflow(src, dst);
dinic.init(n + 2);
for(int i = 1; i <= m; i++) dinic.add_edge(e[i].u, e[i].v, e[i].w), dinic.add_edge(e[i].v, e[i].u, e[i].w);
dinic.add_edge(src, 2, FINF), dinic.add_edge(3, dst, FINF);
double Wmax = dinic.maxflow(src, dst);
double F = std::max(std::min(A * flow, Fmax), flow - Wmax);
double W = flow - F;
// printf("flow = %.3f, Fmax = %.3f, Wmax = %.3f, F = %.3f\n", flow, Fmax, Wmax, F);
dinic.init(n + 2);
for(int i = 1; i <= m; i++) dinic.add_edge(e[i].u, e[i].v, e[i].w), dinic.add_edge(e[i].v, e[i].u, e[i].w);
dinic.add_edge(src, 2, W), dinic.add_edge(src, 1, F), dinic.add_edge(3, dst, FINF);
double ff1 = dinic.maxflow(src, dst);
assert(std::abs(ff1 - flow) <= eps);
for(int i = 2; i <= 4 * m + 1; i += 4) fe[(i + 2) / 4] = dinic.edge[i ^ 1].r - dinic.edge[(i + 2) ^ 1].r;
// for(int i = 1; i <= m; i++) printf("(%d, %d) %.4f\n", e[i].u, e[i].v, fe[i]);
dinic.init(n + 2);
for(int i = 1; i <= m; i++) dinic.add_edge(e[i].u, e[i].v, std::max(fe[i], 0.)), dinic.add_edge(e[i].v, e[i].u, std::max(-fe[i], 0.));
dinic.add_edge(src, 1, F), dinic.add_edge(3, dst, FINF);
double ff2 = dinic.maxflow(src, dst);
// printf("ff = %.3f\n", ff2);
assert(std::abs(ff2 - F) <= eps);
for(int i = 2; i <= 4 * m + 1; i += 4) {
double f = dinic.edge[i ^ 1].r - dinic.edge[(i + 2) ^ 1].r;
printf("%.11f %.11f\n", f / V, fe[(i + 2) / 4] - f);
}
printf("%.11f\n", pow(F / V, A) * pow(flow - F, 1 - A));
return 0;
}
参考资料:
https://www.csc.kth.se/~austrin/icpc/finals2017solutions.pdf
https://cekavis.github.io/icpc-world-finals-2017/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】