集训队作业 2021 #114. Son of Pipe Stream
\(\rm Son ~of ~Pipe~ Stream(DJ)\)
给定一张 \(n\) 个点,\(m\) 条边的图,将这张图进行一次“复制”我们得到了两张完全相同的图,这两张图均以 \(3\) 号点作为汇点,但第一张图以 \(1\) 号节点作为源点,第二张图以第 \(2\) 号节点作为源点。
给定常数 \(v,a\),同时每条边有边权 \(c_i\)
现在,你需要给两张图上的每条边确定一个流量和方向,在两张图上一条边的方向应该是一致的,除源点和汇点每个点需满足流量平衡,同时对于每条边有约束:
- \(v\cdot f_i+w_i\le c_i\)
其中 \(f_i\) 为第一张图上此边的流量 \(w_i\) 为第二张图上此边的流量。
对于流入汇点的流量,设 \(F\) 为第一张图上流入汇点的流量,\(W\) 为第二张图上流入汇点的流量,你需要最大化 \(F^a\cdot W^{1-a}\)
\(n\le 300,m\le \frac{n(n-1)}{2}\),答案的绝对误差不应超过 \(10^{-4}\)
Solution
考虑 \(\frac{v^aF^aW^{1-a}}{v^a}=\frac{(vF)^aW^{1-a}}{v^a}\)
对于每条边,令 \(f_i'=v\cdot f_i\),限制等价于 \(f_i'+w_i\le c_i\)
考虑怎样的一组 \(F,W\) 是合法的,我们先跑出 \(F\) 的上界 \(F_{\max}\),跑出 \(W\) 的上界 \(W_{\max}\),然后跑出 \(S=(F+W)_{\max}\)
可以证明只要一组 \((F',W')\) 满足此约束那么他都是合法的。
证明:
首先可以证明 \((F_{\max},S-F_{\max})\) 是合法的,因为我们可以先流 \(F_{\max}\),然后再将残余网络给第二部分流,因为不会退流给起始点,又因为我们任意增广总是可以流到最大流,所以 \(S-F_{\max}\) 是一定可以取到的。
类似的,\((S-W_{\max},W_{\max})\) 也可以取到。
对于任意的 \((F',W')\),我们考虑这两张图的流量网络,我们通过 \(\alpha\) 和 \(1-\alpha\) 总是能够组成 \((F',W')\),所以最后的策略为先流 \(\alpha \times (F_{\max},S-F_{\max})+(1-\alpha)\times (S-W_{\max}, W_{\max})\),然后得到答案。
等一下,还有一个限制,两张图上的边必须同向。
可以这样考虑,我们先求出 \(F'\) 和 \(W'\) 然后跑一次最大流。
此时我们重构这张图,每条边的方向为此流的方向,每条边的容量为此流中的容量,然后再从 \(1\) 开始跑一次最大流,将边删掉,就是从 \(2\) 出发的流量了。
然后由于 \(S\) 确定,答案相当于 \((F+W)=S,F^aW^{1-a}\) 的最大值,取在 \(F'=a\cdot S\) 处有最大值。
\(Code:\)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define mp make_pair
const double inf = 1e9 ;
const int N = 200 + 5 ;
const int M = 1e5 + 5 ;
int n, m, top ;
struct S { int x, y ; double z ; } s[M], st[M] ;
double K, alpha ;
map<pair<int, int>, int> sf ;
map<pair<int, int>, double> dw ;
map<pair<int, int>, double> cost, FF ;
struct Flow {
int S, T, cnt, dep[N], head[N], cur[N] ;
struct E {
int fr, to, next ; double w ;
} e[M] ;
void undir(int x, int y, double z) { //undircet
e[++ cnt] = (E){ x, y, head[x], z }, head[x] = cnt,
e[++ cnt] = (E){ y, x, head[y], z }, head[y] = cnt ;
}
void dir(int x, int y, double z) { //dircet
e[++ cnt] = (E){ x, y, head[x], z }, head[x] = cnt,
e[++ cnt] = (E){ y, x, head[y], 0 }, head[y] = cnt ;
}
queue<int> q ;
bool bfs() {
rep( i, 0, n ) dep[i] = 0 ; dep[S] = 1, q.push(S) ;
while( !q.empty() ) {
int u = q.front() ; q.pop() ;
Next( i, u ) {
int v = e[i].to ;
if( !dep[v] && e[i].w )
dep[v] = dep[u] + 1, q.push(v) ;
}
} return (dep[T] != 0);
}
double dfs(int x, double dist) {
if(x == T) return dist ; double flow = 0 ;
for(int &i = cur[x]; i; i = e[i].next) {
int v = e[i].to ;
if((dep[v] == dep[x] + 1) && e[i].w ){
double di = dfs(v, min(dist, e[i].w)) ;
e[i].w -= di, e[i ^ 1].w += di,
dist -= di, flow += di ;
if( !dist ) return flow ;
}
} return flow ;
}
double dinic() {
double ans = 0 ;
while(bfs()) {
memcpy(cur, head, sizeof(head)) ;
while(double di = dfs(S, inf)) ans += di ;
} return ans ;
}
void build() {
for(re int i = 2; i <= cnt; ++ i ) {
if( e[i].fr == 0 || e[i].to == 0 ) continue ;
double c = dw[mp(e[i].fr, e[i].to)] ;
if( c <= e[i].w ) continue ;
c -= e[i].w, ++ top ;
st[top].x = e[i].fr, st[top].y = e[i].to, st[top].z = c ;
cost[mp(e[i].fr, e[i].to)] = c ;
cost[mp(e[i].to, e[i].fr)] = - c ;
}
}
void put() {
for(re int i = 2; i <= cnt; i += 2) {
if( e[i].fr == 0 || e[i].to == 0 ) continue ;
double c = e[i ^ 1].w ;
FF[mp(e[i].fr, e[i].to)] = c ;
FF[mp(e[i].to, e[i].fr)] = -c ;
}
}
void init() {
S = 0, T = 3, cnt = 1, memset( head, 0, sizeof(head) ) ;
}
} flow[3] ;
signed main()
{
cin >> n >> m >> K >> alpha ;
rep( i, 0, 2 ) flow[i].init() ;
int x, y ; double z ;
rep( i, 1, m ) {
cin >> x >> y >> z, s[i] = (S){x, y, z} ;
sf[mp(x, y)] = 1, sf[mp(y, x)] = -1 ;
rep( j, 0, 2 ) flow[j].undir(x, y, z) ;
dw[mp(x, y)] = dw[mp(y, x)] = z ;
}
flow[0].S = 1, flow[1].S = 2, flow[2].S = 0 ;
flow[2].undir(0, 1, inf), flow[2].undir(0, 2, inf) ;
double F = flow[0].dinic() ;
double W = flow[1].dinic() ;
double Z = flow[2].dinic() ;
double ll = Z - W, rr = F ;
double ff = max(ll, min(rr, alpha * Z)) ;
double ans = pow(ff, alpha) * pow(Z - ff, 1 - alpha) ;
ans = ans / pow(K, alpha) ;
flow[2].init() ;
rep( i, 1, m )
x = s[i].x, y = s[i].y, z = s[i].z,
flow[2].undir(x, y, z) ;
flow[2].S = 0, flow[2].dir(0, 1, ff), flow[2].dir(0, 2, Z - ff) ;
flow[2].dinic(), flow[2].build(), flow[2].init() ;
rep( i, 1, top )
x = st[i].x, y = st[i].y, z = st[i].z, flow[2].dir(x, y, z) ;
flow[2].dir(0, 1, ff), flow[2].dinic(), flow[2].put() ;
rep( i, 1, m ) {
x = s[i].x, y = s[i].y ;
printf("%.10lf %.10lf\n", FF[mp(x, y)] / K, cost[mp(x, y)] - FF[mp(x, y)] ) ;
}
printf("%.10lf\n", ans ) ;
return 0 ;
}