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 }

spfa

  然后再讲讲费用流做法。因为不存在负的流量,但是我们知道$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 }

原文地址:https://www.cnblogs.com/yyf0309/p/10197979.html

时间: 2024-11-06 22:10:29

bzoj 3597 [Scoi2014] 方伯伯运椰子 - 费用流 - 二分答案的相关文章

BZOJ 3597 SCOI2014 方伯伯送椰子 网络流分析+SPFA

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3597 Description 四川的方伯伯为了致富,决定引进海南的椰子树.方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统. 现在用点来表示交通节点,边来表示道路.这样,方伯伯的椰子园就可以看作一个有 n + 2 个交通节点,m条边的有向无环图.n +1 号点为入口,n +2 号点为出口.每条道路都有 6 个参数,ui,vi,ai,bi,ci,di,分别表示,该道路从 ui 号点通

BZOJ3597: [Scoi2014]方伯伯运椰子

输入格式: 第1 行包含2 个整数n,m接下来m 行代表m 条边,表示这个交通网络.每行6 个整数,表示ui,vi,ai,bi,ci,di.接下来1 行包含1 条边,表示连接起点的边 输出格式: 一个浮点数,保留2 位小数,表示要求的答案,数据保证答案大于0 样例输入: 6 7 1 2 0 0 1 1000 2 4 0 0 1 1000 4 6 0 0 1 1000 1 3 0 0 0 0 3 5 0 0 0 0 5 6 0 0 0 0 6 8 0 0 1 0 7 1 0 0 1 0 样例输出:

BZOJ3597 [Scoi2014]方伯伯运椰子 【二分 + 判负环】

题目链接 BZOJ3597 题解 orz一眼过去一点思路都没有 既然是流量网络,就要借鉴网络流的思想了 我们先处理一下那个比值,显然是一个分数规划,我们二分一个\(\lambda = \frac{X - Y}{k}\) 如果\(\lambda\)成立,则 \[\lambda \le \frac{X - Y}{k}\] 即 \[\lambda k + (Y - X) \le 0\] 所以我们只需要判断是否存在一种方案使得这个式子成立 依照网络流的思想,撤回流量就往反向边走,扩展流量往正向边 对于边

BZOJ 3595: [Scoi2014]方伯伯的Oj SBT+可持久化Treap

3595: [Scoi2014]方伯伯的OjTime Limit: 6 Sec  Memory Limit: 256 MBSubmit: 102  Solved: 54[Submit][Status] Description 方伯伯正在做他的Oj.现在他在处理Oj上的用户排名问题. Oj上注册了n个用户,编号为1-",一开始他们按照编号排名.方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号: 1.操作格式为1 x y,意味着将编号为z的用户编号改为V,而排名不变,执行完该操作后需要

bzoj 3594: [Scoi2014]方伯伯的玉米田 dp树状数组优化

3594: [Scoi2014]方伯伯的玉米田 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 314  Solved: 132[Submit][Status] Description 方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美.这排玉米一共有N株,它们的高度参差不齐.方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列.方伯伯可以选择一个区间,把这个区间的

BZOJ 3594[Scoi2014]方伯伯的玉米田

题面: 3594: [Scoi2014]方伯伯的玉米田 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 1403  Solved: 630[Submit][Status][Discuss] Description 方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美.这排玉米一共有N株,它们的高度参差不齐.方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列.方伯伯可

bzoj 3594 [Scoi2014]方伯伯的玉米田(DP+二维BIT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3594 [题意] 给定一个n个数的序列,有K次将一个区间内的数加1的机会,问最长不下降子序列. [思路] 首先知道每次加1一个区间为[i,n]肯定不会差. 设f[i][j]为前i个数,还有j次机会的LIS,则有转移式: f[i][j] = max{ f[a][b],h[a]+b<=h[i]+j } 则可以用二维BIT加速方程转移. [代码] 1 #include<set> 2

BZOJ 3594 [Scoi2014]方伯伯的玉米田(二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3594 [题目大意] 给出一个数列,选出k个区间使得区间内数全部加1, 求k次操作之后最长的不下降子序列 [题解] 我们发现,每次的区间右端点一定贪心选到最右端才是最优的, 那么在用树状数组统计的时候,只要一个点被+1了,那么后面的点起码+1, 我们在树状数组后面加一维统计被区间包含的次数,发现也是前缀和关系, 所以用二维树状数组统计答案即可. 为避免自身被重复统计,第二维循环降序.

BZOJ 3594 Scoi2014 方伯伯的玉米田 树状数组

题目大意:给定一个序列,可以选择k次区间并将区间内每个数都+1,求操作之后LIS的最大值 我的做法不是标解...5E的复杂度为何跑的飞起... 首先一个显而易见的结论就是我们选择的k次区间右端点都是n时才能保证最优 知道这个我们就可以DP了- - 令f[i][j]表示前i个数上升j次的最大LIS 那么有f[i][j]=max{f[k][l]|k<i,l<=j,a[k]+l<=a[i]+j}+1 看到三维偏序就可以用二维树状数组了- - 时间复杂度O(nklog(max(ai)+k)log