很厉害的题目。
首先发现
v
v
v是没有意义的,我们可以将Flubber流量乘上
v
v
v,最后除
v
a
v^a
va即可。
我们先算出只考虑Flubber的最大流量
A
A
A,只考虑水的最大流量
B
B
B,以及总的最大流量
C
C
C,显然
C
≥
max
(
A
,
B
)
C\geq \max(A,B)
C≥max(A,B)。那么我们可以发现一个性质:我们可以任意分配
C
C
C给两种物质,只要满足分配给Flubber的
W
≤
A
W\leq A
W≤A,分配给水的
C
−
W
≤
B
C-W\leq B
C−W≤B即可。证明可以考虑最大流最小割定理:我们先加入一个超级源点
S
S
S,
S
→
1
S\to1
S→1和
S
→
2
S\to 2
S→2的边容量都是
+
∞
+\infty
+∞,那么此时
S
S
S到
3
3
3的最大流和最小割都是
C
C
C。若我们固定Flubber的流量
W
∈
[
C
−
B
,
A
]
W\in[C-B,A]
W∈[C−B,A],也即将
S
→
1
S\to 1
S→1的边容量改为
W
W
W,考虑是否割去这条边,可以得到新的最小割仍为
min
(
W
+
B
,
C
)
=
C
\min(W+B,C)=C
min(W+B,C)=C,也即此时最大流仍为
C
C
C,于是一定有合法方案。
这样问题就很简单了。我们最终的总流量一定是
C
C
C,需要找出最优的
W
W
W,对答案形式
W
a
⋅
(
C
−
W
)
1
−
a
W^a\cdot(C-W)^{1-a}
Wa⋅(C−W)1−a求导可以发现
W
W
W最接近
a
⋅
C
a\cdot C
a⋅C的时候最优。
还有一个问题是输出方案需要满足Flubber和水同向,可以这么解决:我们先将所有边设为无向的,然后跑出最优的W对应的每条边总流量,并将每条边根据流量定向,再跑一次
1
1
1到
3
3
3的大小为
W
W
W的流即可知道每条边流量分配。
时间复杂度为
O
(
n
2
p
)
\mathcal O(n^2p)
O(n2p),不过实际非常快。
#include <bits/stdc++.h>
#define eps 1e-7
using namespace std;
typedef double db;
struct Edge {
int t,next;
db f;
Edge() {}
Edge(int a,db b,int c):t(a),f(b),next(c) {}
};
Edge e[1000005];
int head[305],vs,vt,tot=-1;
inline void addEdge(int x,int y,db z) {
e[++tot]=Edge(y,z,head[x]);
head[x]=tot;
e[++tot]=Edge(x,z,head[y]);
head[y]=tot;
}
namespace Flow {
int d[305],cur[305];
queue <int> q;
bool bfs() {
while (!q.empty()) q.pop();
memset(d,255,sizeof(d));
d[vs]=0;cur[vs]=head[vs];
q.push(vs);
while (!q.empty()) {
int x=q.front();q.pop();
for(int i=head[x];i!=-1;i=e[i].next)
if (e[i].f>eps&&d[e[i].t]==-1) {
int u=e[i].t;
d[u]=d[x]+1;
cur[u]=head[u];
if (u==vt) return 1;
q.push(u);
}
}
return 0;
}
db dfs(int x,db a) {
if (x==vt||a<eps) return a;
db ans=0;
for(int &i=cur[x];i!=-1;i=e[i].next)
if (e[i].f>eps&&d[e[i].t]==d[x]+1) {
int u=e[i].t;
db f=dfs(u,min(a,e[i].f));
if (f) {
e[i].f-=f;
e[i^1].f+=f;
ans+=f;
a-=f;
if (!a) break;
}
}
return ans;
}
db maxflow(db up) {
db ans=0;
while (bfs()&&up-ans>eps)
ans+=dfs(vs,up-ans);
return ans;
}
}
bool col[100005];
int main() {
memset(head,255,sizeof(head));
int n,m;
db v,a;
scanf("%d%d%lf%lf",&n,&m,&v,&a);
for(int i=1;i<=m;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addEdge(x,y,z);
}
db sum=0,allx=0,ally=0;
vs=1;vt=3;
allx=Flow::maxflow(1e18);
sum+=allx;
vs=2;vt=3;
sum+=Flow::maxflow(1e18);
for(int i=0;i<=tot;i+=2) {
db u=e[i].f,v=e[i^1].f;
e[i].f=e[i^1].f=(u+v)*.5;
}
vs=2;vt=3;
ally=Flow::maxflow(1e18);
for(int i=0;i<=tot;i+=2) {
db u=e[i].f,v=e[i^1].f;
e[i].f=e[i^1].f=(u+v)*.5;
}
db w;
if (sum-ally>a*sum) w=sum-ally;
else if (allx<a*sum) w=allx;
else w=a*sum;
vs=1;vt=3;
Flow::maxflow(w);
vs=2;vt=3;
Flow::maxflow(sum-w);
for(int i=0;i<=tot;i+=2) {
db u=e[i].f,v=e[i^1].f;
if (u<=v) {
e[i].f=(v-u)*.5;
e[i^1].f=0;
col[i>>1]=0;
}
else {
e[i^1].f=(u-v)*.5;
e[i].f=0;
col[i>>1]=1;
}
}
vs=1;vt=3;
Flow::maxflow(w);
for(int i=0;i<m;i++)
if (!col[i]) printf("%.10f %.10f\n",e[2*i+1].f/v,e[2*i].f);
else printf("%.10f %.10f\n",-e[2*i].f/v,-e[2*i+1].f);
printf("%.10f\n",pow(w/v,a)*pow(sum-w,1.0-a));
return 0;
}