洛谷 P6938 [ICPC2017 WF]Son of Pipe Stream 题解
一、题目:
二、思路:
这道题整整让我调了一个下午!一直被精度卡,后来把所有的精度全部取消就 A 了!😱
现在让我们来系统梳理一下这道题的思路。
首先可以看出,题目中的参数 \(v\) 纯粹是迷惑人的。我们完全可以将 \(v\times f\) 看成一个新流。最终求出来答案再除以 \(v^a\) 即可。
我们试想这样一种情况:新建一个超级源点 \(\mathfrak{s}\),从 \(\mathfrak{s}\) 分别向 1、2 号点连容量是无穷大的边,设从 \(\mathfrak{s}\) 向 3 号点跑最大流得到的答案是 \(Z\)。那么一定有 \(F+W=Z\)。
最终的答案设成一个函数 \(f(F)=F^a(Z-F)^{1-a}\),对该函数求导,得
则当 \(F<aZ\) 时,\(f'(F)>0\);当 \(F>aZ\) 时,\(f'(F)<0\)。所以当 \(F=aZ\) 时,\(f(F)\) 有极大值。
但是接下来有两个问题亟待我们去解决。
第一个问题是我们可能无法让 \(F\) 达到 \(aZ\)。设从 1 号点向 3 号点跑最大流的答案是 \(F_{\max}\),从 2 号点向 3 号点跑最大流的答案是 \(W_{\max}\)。那么,\(F\) 的范围(或者可以理解为定义域)为 \([Z-W_{\max},F_{\max}]\)。所以我们只需要在 \(F\) 的定义域找见距离 \(aZ\) 最近的值即可。设该值为 \(F^*\),\(W^*=Z-F^*\)。
第二个问题是,有可能 \(F^*\) 所对应的流和 \(W^*\) 所对应的流叠加在一起时,会造成同一根水管会有不同方向的两条流。但是,我们可以构造出一种满足题意的流。
- 从 \(\mathfrak{s}\) 向 1 号点连容量为 \(F^*\) 的边,向 2 号点连容量是 \(W^*\) 的边。再从 \(\mathfrak{s}\) 向 3 号点跑最大流。
- 新建一张图 \(G\),新图边的方向为旧图中最大流的流向,容量为旧图中最大流的流量。
- 从新图的超级源点 \(\mathfrak{s}\) 向 1 号点连容量为 \(F^*\) 的边,再从 \(\mathfrak{s}\) 向 3 号点跑最大流。此时每条边的流量就是 Flubber 的最终答案,每条边的容量减流量就是 water 的最终答案。
显然,由于新图的每条边的流量守恒,容量也守恒,所以 water 的流量也是守恒的。而且 Flubber 的流量一定是 \(F^*\),water 的流量一定是 \(W^*\)。
三、代码:
#include <iostream> // 不要设任何的eps!
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 205, MAXM = MAXN * MAXN;
const double INF = 1e9;
int n, m, head[MAXN], tot = 1, S, T;
int q[MAXN], cur[MAXN], d[MAXN];
int dir[MAXM];
double Fmax, Wmax, Z, F$, W$, ans, v, a;
struct Edge {
int y, next;
double w;
Edge() {}
Edge(int _y, int _next, double _w) : y(_y), next(_next), w(_w) {}
}e[MAXM];
inline void add(int x, int y, double w) {
e[++ tot] = Edge(y, head[x], w);
head[x] = tot;
}
inline void connect(int x, int y, double w) {
add(x, y, w); add(y, x, w);
}
bool bfs(void) {
int hh = 0, tt = -1;
q[++ tt] = S; cur[S] = head[S];
mem(d, -1); d[S] = 0;
while (hh <= tt) {
int x = q[hh ++];
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (e[i].w > 0 && d[y] == -1) {
d[y] = d[x] + 1;
cur[y] = head[y];
if (y == T) return true;
q[++ tt] = y;
}
}
}
return false;
}
double find(int x, double limit) {
if (x == T) return limit;
double flow = 0;
for (int i = cur[x]; i && flow < limit; i = e[i].next) {
cur[x] = i;
int y = e[i].y;
if (e[i].w > 0 && d[y] == d[x] + 1) {
double t = find(y, min(e[i].w, limit - flow));
if (t == 0) d[y] = -1;
e[i].w -= t; e[i ^ 1].w += t; flow += t;
}
}
return flow;
}
double dinic(void) {
double r = 0, flow;
while (bfs()) {
if ((flow = find(S, INF)) > 0) r += flow;
}
return r;
}
inline void restore(void) {
for (int i = 2; i <= tot; i += 2) {
double mid = (e[i].w + e[i ^ 1].w) / 2.0;
e[i].w = e[i ^ 1].w = mid;
}
}
int main() {
n = read(); m = read(); scanf("%lf%lf", &v, &a);
for (int i = 1; i <= m; ++ i) {
int x = read(), y = read();
double w; scanf("%lf", &w);
connect(x, y, w);
}
S = 1; T = 3;
Fmax = dinic(); restore();
S = 2;
Wmax = dinic(); restore();
S = n + 1; connect(S, 1, INF); connect(S, 2, INF);
Z = dinic(); restore();
if (Z - Wmax > a * Z) F$ = Z - Wmax;
else if (Fmax < a * Z) F$ = Fmax;
else F$ = a * Z;
W$ = Z - F$;
ans = pow(F$, a) * pow(W$, 1 - a);
ans /= pow(v, a);
for (int i = head[n + 1]; i; i = e[i].next) {
int y = e[i].y;
if (y == 1) e[i].w = F$, e[i ^ 1].w = 0;
else e[i].w = W$, e[i ^ 1].w = 0;
}
dinic();
for (int i = 2; i <= tot; i += 2) {
double mid = (e[i].w + e[i ^ 1].w) / 2;
if (e[i ^ 1].w > mid) { dir[i] = 1; e[i].w = e[i ^ 1].w - mid; e[i ^ 1].w = 0; }
else { dir[i] = -1; e[i ^ 1].w = e[i].w - mid; e[i].w = 0; }
}
for (int i = head[n + 1]; i; i = e[i].next) {
int y = e[i].y;
if (y == 1) { e[i].w = F$; e[i ^ 1].w = 0; }
else e[i].w = e[i ^ 1].w = 0;
}
S = n + 1;
dinic();
tot -= 4;
for (int i = 2; i <= tot; i += 2) {
if (dir[i] == 1)
printf("%.8lf %.8lf\n", e[i ^ 1].w / v, e[i].w);
else
printf("-%.8lf -%.8lf\n", e[i].w / v, e[i ^ 1].w);
}
printf("%.8lf\n", ans);
return 0;
}