bzoj 3597 [Scoi2014] 方伯伯运椰子 - 费用流 - 二分答案
题目传送门
题目大意
给定一个费用流,每条边有一个初始流量$c_i$和单位流量费用$d_i$,增加一条边的1单位的流量需要花费$b_i$的代价而减少一条边的1单位的流量需要花费$a_i$的代价。要求最小化总费用减少量和调整次数的比值(至少调整一次)。
根据基本套路,二分答案,移项,可以得到每条边的贡献。
设第$i$条边的流量变化量为$m_i$,每次变化花费的平均费用为$w_i$。那么有
$\sum c_id_i - \sum (c_i + m_i)d_i + |m_i|(w_i + mid) > 0$
$\sum m_id_i+ |m_i|(w_i + mid) < 0$
那么二分答案后等于判断是否存在一个增广环的费用和为负。(请手动参见式子脑补边权)。
然后可以写个spfa。
Code
1 /** 2 * bzoj 3 * Problem#3597 4 * Accepted 5 * Time: 464ms 6 * Memory: 1720k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #include <vector> 12 #include <queue> 13 using namespace std; 14 typedef bool boolean; 15 16 template <typename T> 17 void pfill(T* pst, const T* ped, T val) { 18 for ( ; pst != ped; *(pst++) = val); 19 } 20 21 typedef class Edge { 22 public: 23 int ed, nx; 24 double w; 25 26 Edge(int ed, int nx, double w) : ed(ed), nx(nx), w(w) { } 27 } Edge; 28 29 typedef class MapManager { 30 public: 31 int* h; 32 vector<Edge> es; 33 34 MapManager() { } 35 MapManager(int n) { 36 h = new int[(n + 1)]; 37 pfill(h, h + n + 1, -1); 38 } 39 ~MapManager() { 40 delete[] h; 41 es.clear(); 42 } 43 44 void addEdge(int u, int v, double w) { 45 es.push_back(Edge(v, h[u], w)); 46 h[u] = (signed) es.size() - 1; 47 } 48 49 Edge& operator [] (int p) { 50 return es[p]; 51 } 52 } MapManager; 53 54 const double dinf = 1e10; 55 const double eps = 1e-4; 56 57 typedef class Graph { 58 public: 59 int n, s; 60 MapManager g; 61 62 int *cnt; 63 double *f; 64 boolean *vis; 65 66 Graph(int n, int s) : n(n), s(s), g(n) { 67 cnt = new int[(n + 1)]; 68 f = new double[(n + 1)]; 69 vis = new boolean[(n + 1)]; 70 pfill(vis, vis + n + 1, false); 71 } 72 73 boolean neg_circle() { 74 static queue<int> que; 75 pfill(f, f + n + 1, dinf); 76 pfill(cnt, cnt + n + 1, 0); 77 f[s] = 0, cnt[s]++; 78 que.push(s); 79 while (!que.empty()) { 80 int e = que.front(); 81 que.pop(); 82 vis[e] = false; 83 for (int i = g.h[e], eu; ~i; i = g[i].nx) { 84 eu = g[i].ed; 85 double w = f[e] + g[i].w; 86 if (w < f[eu]) { 87 f[eu] = w, cnt[eu]++; 88 if (cnt[eu] > n) 89 return true; 90 if (!vis[eu]) { 91 que.push(eu); 92 vis[eu] = true; 93 } 94 } 95 } 96 } 97 return false; 98 } 99 } Graph; 100 101 int n, m; 102 int *u, *v, *a, *b, *c, *d; 103 double init_ans = 0.0; 104 105 inline void init() { 106 scanf("%d%d", &n, &m); 107 u = new int[(m + 1)]; 108 v = new int[(m + 1)]; 109 a = new int[(m + 1)]; 110 b = new int[(m + 1)]; 111 c = new int[(m + 1)]; 112 d = new int[(m + 1)]; 113 for (int i = 1; i <= m; i++) { 114 scanf("%d%d%d%d%d%d", u + i, v + i, a + i, b + i, c + i, d + i); 115 init_ans += c[i] * d[i]; 116 } 117 } 118 119 boolean check(double mid) { 120 Graph graph(n + 2, n + 1); 121 MapManager& g = graph.g; 122 123 for (int i = 1; i <= m; i++) { 124 if (u[i] == n + 1) { 125 g.addEdge(u[i], v[i], 0); 126 } else if (c[i]) { 127 g.addEdge(u[i], v[i], d[i] + b[i] + mid); 128 g.addEdge(v[i], u[i], -d[i] + a[i] + mid); 129 } else { 130 g.addEdge(u[i], v[i], d[i] + b[i] + mid); 131 } 132 } 133 134 return graph.neg_circle(); 135 } 136 137 inline void solve() { 138 double l = 0, r = init_ans, mid; 139 for (int t = 0; t < 128 && l < r - eps; t++) { 140 mid = (l + r) / 2; 141 if (check(mid)) 142 l = mid; 143 else 144 r = mid; 145 } 146 printf("%.2lf\n", l); 147 } 148 149 int main() { 150 init(); 151 solve(); 152 return 0; 153 }
然后再讲讲费用流做法。因为不存在负的流量,但是我们知道$c_i' \geqslant 0$,因此我们考虑将$m_i$变成$-c_i + m_i'$。
其中$m_i'$始终非负。可以理解为这个做法就是将所有边的流量先变为0再重新调整。
对于绝对值的部分稍微处理一下就行了。(之前那个式子脑残取了等,调了半天,sad。。。)
Code
1 /** 2 * bzoj 3 * Problem#3597 4 * Accepted 5 * Time: 1168ms 6 * Memory: 2004k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #include <queue> 12 #include <cmath> 13 using namespace std; 14 typedef bool boolean; 15 16 template <typename T> 17 void pfill(T* pst, const T* ped, T val) { 18 for ( ; pst != ped; *(pst++) = val); 19 } 20 21 typedef class Edge { 22 public: 23 int ed, nx, r; 24 double c; 25 26 Edge(int ed = 0, int nx = 0, int r = 0, double c = 0) : ed(ed), nx(nx), r(r), c(c) { } 27 } Edge; 28 29 typedef class MapManager { 30 public: 31 int* h; 32 vector<Edge> es; 33 34 MapManager() { } 35 MapManager(int n) { 36 h = new int[(n + 1)]; 37 pfill(h, h + n + 1, -1); 38 } 39 ~MapManager() { 40 delete[] h; 41 es.clear(); 42 } 43 44 void addEdge(int u, int v, int r, double c) { 45 es.push_back(Edge(v, h[u], r, c)); 46 h[u] = (signed) es.size() - 1; 47 } 48 49 void addArc(int u, int v, int cap, double c) { 50 addEdge(u, v, cap, c); 51 addEdge(v, u, 0, -c); 52 } 53 54 Edge& operator [] (int p) { 55 return es[p]; 56 } 57 } MapManager; 58 59 const signed int inf = (signed) (~0u >> 1); 60 const double dinf = 1e10; 61 const double eps = 1e-4; 62 63 template <typename T> 64 T __abs(T x) { 65 return (x < 0) ? (-x) : (x); 66 } 67 68 typedef class Network { 69 public: 70 int S, T; 71 MapManager g; 72 73 int *le; 74 int *mf; 75 double *f; 76 boolean *vis; 77 78 Network() { } 79 // be sure T is the last node 80 Network(int S, int T) : S(S), T(T), g(T) { 81 f = new double[(T + 1)]; 82 le = new int[(T + 1)]; 83 mf = new int[(T + 1)]; 84 vis = new boolean[(T + 1)]; 85 pfill(vis, vis + T, false); 86 } 87 ~Network() { 88 delete[] f; 89 delete[] le; 90 delete[] mf; 91 delete[] vis; 92 } 93 94 double spfa() { 95 double w; 96 static queue<int> que; 97 pfill(f, f + T + 1, dinf); 98 que.push(S); 99 f[S] = 0, le[S] = -1, mf[S] = inf; 100 while (!que.empty()) { 101 int e = que.front(); 102 que.pop(); 103 vis[e] = false; 104 for (int i = g.h[e], eu; ~i; i = g[i].nx) { 105 if (!g[i].r) 106 continue; 107 eu = g[i].ed, w = f[e] + g[i].c; 108 if (w < f[eu]) { 109 f[eu] = w, le[eu] = i, mf[eu] = min(mf[e], g[i].r); 110 if (!vis[eu]) { 111 vis[eu] = true; 112 que.push(eu); 113 } 114 } 115 } 116 } 117 if (__abs(f[T] - dinf) < eps) 118 return dinf; 119 double rt = 0; 120 for (int p = T, e; ~le[p]; p = g[e ^ 1].ed) { 121 e = le[p]; 122 g[e].r -= mf[T]; 123 g[e ^ 1].r += mf[T]; 124 rt += mf[T] * g[e].c; 125 } 126 return rt; 127 } 128 129 double min_cost() { 130 double rt = 0, delta; 131 while (__abs((delta = spfa()) - dinf) >= eps) { 132 rt += delta; 133 } 134 return rt; 135 } 136 } Network; 137 138 // S: n + 1, T: n + 2 139 int n, m; 140 int *u, *v, *a, *b, *c, *d; 141 double init_ans = 0.0; 142 143 inline void init() { 144 scanf("%d%d", &n, &m); 145 u = new int[(m + 1)]; 146 v = new int[(m + 1)]; 147 a = new int[(m + 1)]; 148 b = new int[(m + 1)]; 149 c = new int[(m + 1)]; 150 d = new int[(m + 1)]; 151 for (int i = 1; i <= m; i++) { 152 scanf("%d%d%d%d%d%d", u + i, v + i, a + i, b + i, c + i, d + i); 153 init_ans += c[i] * d[i]; 154 } 155 } 156 157 boolean check(double mid) { 158 double cost = 0; 159 Network network(n + 1, n + 2); 160 MapManager& g = network.g; 161 for (int i = 1; i <= m; i++) { 162 if (u[i] == n + 1) { 163 g.addArc(u[i], v[i], c[i], 0); 164 } else { 165 g.addArc(u[i], v[i], c[i], -(a[i] + mid) + d[i]); 166 g.addArc(u[i], v[i], inf, b[i] + mid + d[i]); 167 // cost += c[i] * (-d[i] + a[i] + mid); 168 cost += c[i] * (-d[i] + a[i] + mid); 169 } 170 } 171 cost += network.min_cost(); 172 return cost < 0; 173 } 174 175 inline void solve() { 176 double l = 0, r = init_ans, mid; 177 for (int t = 0; t < 128 && l < r - eps; t++) { 178 mid = (l + r) / 2; 179 if (check(mid)) 180 l = mid; 181 else 182 r = mid; 183 } 184 printf("%.2lf\n", l); 185 } 186 187 int main() { 188 init(); 189 solve(); 190 return 0; 191 }