首页 > 技术文章 > Luogu2570 [ZJOI2010]贪吃的老鼠 ---- 网络流

CreeperLKF 2018-06-12 21:07 原文

Luogu2570  [ZJOI2010]贪吃的老鼠

题面描述

https://www.luogu.org/problemnew/show/P2570

然后题意大概就是m只老鼠,然后吃n个奶酪,已知

  • 每个奶酪体积为$p_i$
  • 每个奶酪原始保质期为$s_i到t_i$(在一个时间轴上的位置)
  • 每只老鼠吃奶酪的速度为$v_i$

要求

  • 同一时刻老鼠不能被原谅(不能吃两块奶酪)
  • 同一时刻奶酪不能原谅(不能被两只老鼠吃)

老鼠吃奶酪的时间可以为小数,且一个老鼠“一生”可以吃多块奶酪,一块奶酪“一生”可以多次被吃

现在你可以为奶酪$+T\ s$,也就是对于所有奶酪$t_i += T$

现在让你最小化$T$

题解

<!--瞎BB开始-->

好像机房就我没写了......

反正是要看题解的,这辈子逃不开题解的......

但题解看不懂连生活都维持不了这样子......
<!--瞎BB结束-->

还是DL不能依靠,还是自己看比较好

二分答案+最大流

建图方式:

首先对所有时间点离散化,记两个时间点之间的时间段长度为$tim$,然后把老鼠从大到小排序最后加个数字0,差分得到m个差分值,记为d,依次编号为$1,2,3...$

对奶酪建点,对时间点间的时间段拆点,拆成m个老鼠差分值

源点连向奶酪,容量 p

奶酪连向奶酪保质期内的时间段的老鼠差分点,容量为 老鼠差分值*时间段长度

老鼠差分点连向终点,容量为 老鼠差分值*时间段长度*老鼠差分点编号(排序后从大到小)

然后跑最大流,奶酪满流即为合法

下面配合一个实例来讲解证明为什么这样是对的(反正我是想不到的)

为了区分老鼠速度和差分后的速度,我们将拆出来的点称为“老鼠差分点“或”老鼠点“或指代意义的”点“

举个例子

老鼠分别有速度7,4,2

差分得到3,2,2

然后我们假设时间段长度为2

然后老鼠到t的流量限制为6,8,12

然后和奶酪的流量限制为6,4,4​

 

当且仅当这张图所有奶酪到起点的边满流的时候有解,其中如果一个老鼠$x$在一个时间段内吃了奶酪$y$,那么从该时间段$m-x$到$m$的老鼠差分点到奶酪$y$都会有流量$t*d_i$。这张图的工作原理是比较简单的(如果看不懂可以参考一下其它DL的博客)

最主要难以证明的是题目需要满足的两个要求。

  首先证明这张图满足一个老鼠同一时间只吃一个奶酪

  如果一个从大到小排名第$k$的老鼠吃了同一时间段的$x$块奶酪(如果不在同一时间段的话就不会有非法状态,如果有重叠则重叠部分和这个问题等价),设第$i$块奶酪吃了时间$t_i$,那么我们假设一个非法状态,也就是$(\Sigma{t_i}) > tim$,也就是一个老鼠同时吃多块奶酪,所以这个时候k号老鼠差分点产生的流量至少为$\Sigma{(t_i)}*d_k$,我们记为流量,但是我们对该老鼠的限制有$k*tim*d_k$,我们记为容量,我们要证明非法状态的$流量 > 容量$,在网络流中不存在。

这个时候我们有两种情况:

  1. 速度更快的老鼠还可以吃且可以吃超额部分(超额部分就是引起一只老鼠需要在同一时间吃两个奶酪的部分),那么就可以分担这个老鼠的任务,所以不存在这样的非法状态
  2. 速度更快的老鼠吃不完超额部分,那么这些老鼠一定是已经吃过了,所以根据差分上面$k-1$个老鼠差分点对这个老鼠差分点产生了流量负担,这个负担加上原有的流量为$\Sigma{(t_i)}*d_k+(k-1)*d_k*tim=k*tim*t_k+(\Sigma{(t_i)}-tim)$,由于$(\Sigma{(t_i)}-tim)>0$,所以$\Sigma{(t_i)}*d_k+(k-1)*d_k*tim>k*d_k*tim$,所以$流量 > 容量$,在网络流中无法实现

  然后证明这张图满足一个奶酪同时只被一只老鼠吃

  这个比较简单,一样假设一共有x只老鼠吃了奶酪,每一个吃了时间$t_i$,然后假设非法状态$(\Sigma{t_i}) > tim$,然后由于排名靠前的老鼠吃了的话那么在差分点中对排名较后的老鼠也会有时间上的影响,也就是吃了同一个奶酪的排名最后的老鼠流量为$\Sigma{(t_i)}*d_k$,大于边的容量$ti*d_k$,所以状态不存在。

 

代码如下:

  1 #include <cstdio>
  2 #include <cctype>
  3 #include <cstring>
  4 #include <iostream>
  5 
  6 //User's Lib
  7 
  8 #include <cmath>
  9 #include <algorithm>
 10 
 11 using namespace std;
 12 
 13 #define DEBUG_PORT
 14 #define DEBUG
 15 
 16 #ifdef ONLINE_JUDGE
 17 #undef DEBUG_PORT
 18 #undef DEBUG
 19 #endif
 20 
 21 #ifdef DEBUG_PORT
 22 #if __cplusplus >= 201103L
 23 #ifdef DEBUG
 24 template<typename T>
 25 extern inline void Debug(T tar){
 26     cerr << tar << endl;
 27 }
 28 template<typename Head, typename T, typename... Tail>
 29 extern inline void Debug(Head head, T mid, Tail... tail){
 30     cerr << head << ' ';
 31     Debug(mid, tail...);
 32 }
 33 #else
 34 # pragma GCC diagnostic push
 35 # pragma GCC diagnostic ignored "-Wunused-parameter"
 36 template<typename Head, typename T, typename... Tail>
 37 extern inline void Debug(Head head, T mid, Tail... tail){
 38     return ;
 39 }
 40 # pragma GCC diagnostic pop
 41 # pragma message "Warning : pragma used"
 42 #endif
 43 #else
 44 # pragma message "Warning : C++11 Not Use"
 45 #ifdef DEBUG
 46 template <typename T>
 47 extern inline void Debug(T tar){
 48     cerr << tar << endl;
 49 }
 50 #else
 51 # pragma GCC diagnostic push
 52 # pragma GCC diagnostic ignored "-Wunused-parameter"
 53 template <typename T>
 54 extern inline void Debug(T tar){
 55     return ;
 56 }
 57 # pragma GCC diagnostic pop
 58 # pragma message "Warning : pragma used"
 59 #endif
 60 #endif
 61 #else
 62 # pragma GCC diagnostic push
 63 # pragma GCC diagnostic ignored "-Wunused-parameter"
 64 template<typename Head, typename T, typename... Tail>
 65 extern inline void Debug(Head head, T mid, Tail... tail){
 66     return ;
 67 }
 68 template <typename T>
 69 extern inline void Debug(T tar){
 70     return ;
 71 }
 72 # pragma GCC diagnostic pop
 73 # pragma message "Warning : pragma used"
 74 #endif
 75 
 76 char buf[11111111], *pc = buf;
 77 
 78 extern inline void Main_Init(){
 79     static bool INITED = false;
 80     if(INITED) fclose(stdin), fclose(stdout);
 81     else {
 82         fread(buf, 1, 11111111, stdin); 
 83         INITED = true;           
 84     }
 85 }
 86 
 87 static inline int read(){
 88     int num = 0;
 89     char c, sf = 1;
 90     while(isspace(c = *pc++));
 91     if(c == 45) sf = -1, c = *pc ++;
 92     while(num = num * 10 + c - 48, isdigit(c = *pc++));
 93     return num * sf;
 94 }
 95 
 96 namespace LKF{
 97     template <typename T>
 98     extern inline T abs(T tar){
 99         return tar < 0 ? -tar : tar;
100     }
101     template <typename T>
102     extern inline void swap(T &a, T &b){
103         T t = a;
104         a = b;
105         b = t;
106     }
107     template <typename T>
108     extern inline void upmax(T &x, const T &y){
109         if(x < y) x = y;
110     }
111     template <typename T>
112     extern inline void upmin(T &x, const T &y){
113         if(x > y) x = y;
114     }
115     template <typename T>
116     extern inline T max(T a, T b){
117         return a > b ? a : b;
118     }
119     template <typename T>
120     extern inline T min(T a, T b){
121         return a < b ? a : b;
122     }
123 }
124 
125 //Source Code
126 
127 const int MAXK = 33;
128 const int MAXN = 2018;
129 const int MAXM = 99999;
130 const double INF = 1e16;
131 const double eps = 1e-6;
132 
133 inline bool comp(const double &a, const double &b){
134     double tmp = a - b;//int???
135     if(fabs(tmp) < eps) return 0;
136     return a > b ? 1 : -1;
137 }
138 
139 int s = MAXN - 10, t = s + 1;
140 
141 struct Queue{
142     int s, t;
143     int q[MAXN];
144     Queue(){s = 1, t = 0;}
145     inline void clear(){
146         s = 1, t = 0;
147     }
148     inline bool empty(){
149         return s > t;
150     }
151     inline int size(){
152         return t - s + 1;
153     }
154     inline void push(int tar){
155         q[++ t] = tar;
156     }
157     inline int front(){
158         return q[s];
159     }
160     inline void pop(){
161         s ++;
162     }
163 };
164 
165 struct Graph{
166     int tot;
167     int beginx[MAXN], endx[MAXM], nxt[MAXM];
168     double res[MAXM];
169     Graph(){
170         tot = 1;
171     }
172     inline void Init(){
173         tot = 1;
174         memset(beginx, 0, sizeof(beginx));
175     }
176     inline void add_edge(int u, int v, double r){
177         // Debug(u, "->", v, "[label = \"", r, "\"]");//Debug...
178         nxt[++ tot] = beginx[u], beginx[u] = tot, endx[tot] = v, res[tot] = r;
179         nxt[++ tot] = beginx[v], beginx[v] = tot, endx[tot] = u, res[tot] = 0;
180     }
181 };
182 
183 struct ISap{
184     Graph g;
185     Queue mession;
186     double max_f;
187     int cur[MAXN], d[MAXN], num[MAXN], pre[MAXN];
188     inline void bfs(){
189         mession.clear();
190         mession.push(t);
191         memset(d, 0, sizeof(d));
192         memset(num, 0, sizeof(num));
193         d[t] = 1;
194         int u, v;
195         while(!mession.empty()){
196             u = mession.front();
197             mession.pop();
198             num[d[u]] ++;
199             for(int i = g.beginx[u]; i; i = g.nxt[i]){
200                 v = g.endx[i];
201                 if(!d[v] && comp(g.res[i ^ 1], 0)){
202                     d[v] = d[u] + 1;
203                     mession.push(v);
204                 }
205             }
206         }
207     }
208     inline double dfs(int u, double now_f){
209         if(u == t) return now_f;
210         double ret_f = 0;
211         for(int &i = cur[u]; i; i = g.nxt[i]){
212             int v = g.endx[i];
213             if(comp(g.res[i], 0) && d[u] == d[v] + 1){
214                 double ret = dfs(v, min(g.res[i], now_f));
215                 ret_f += ret, now_f -= ret;
216                 g.res[i] -= ret, g.res[i ^ 1] += ret;
217                 if(d[s] >= MAXN - 4 || !comp(now_f, 0)) return ret_f;
218             }
219         }
220         if(-- num[d[u]] == 0) d[s] = MAXN - 4;
221         ++ num[++ d[u]];
222         cur[u] = g.beginx[u];
223         return ret_f;
224     }
225     inline double ISAP(){
226         bfs();
227         max_f = 0;
228         memcpy(cur, g.beginx, sizeof(cur));
229         while(d[s] < MAXN - 5)
230             max_f += dfs(s, INF);
231         return max_f;
232     }
233 }isap;
234 
235 int n, m, sum;
236 int p[MAXK], r[MAXK], d[MAXK], ss[MAXK];
237 double tmp_arr[MAXK << 1];
238 int cnt;
239 
240 inline bool check(double tar){
241     cnt = 0;
242     isap.g.Init();
243     for(int i = 1; i <= n; i++)
244         tmp_arr[++ cnt] = r[i], tmp_arr[++ cnt] = d[i] + tar;
245     sort(tmp_arr + 1, tmp_arr + 1 + cnt);
246     cnt = unique(tmp_arr + 1, tmp_arr + 1 + cnt) - tmp_arr - 1;
247     for(int i = 1; i <= n; i++)
248         isap.g.add_edge(s, i, p[i]);
249     for(int i = 2; i <= cnt; i++){
250         double lst = tmp_arr[i - 1], tim = tmp_arr[i] - lst;
251         for(int j = 1; j <= m; j++)
252             isap.g.add_edge(n + (i - 2) * m + j, t, j * tim * ss[j]);
253         for(int j = 1; j <= n; j++)
254             if(r[j] <= lst && d[j] + tar >= tmp_arr[i])
255                 for(int k = 1; k <= m; k++)
256                     isap.g.add_edge(j, n + (i - 2) * m + k, tim * ss[k]);
257     }
258     return !comp(isap.ISAP(), sum);
259 }
260 
261 int main(){
262     Main_Init();
263     int T = read();
264     while(T --){
265         n = read(), m = read();
266         sum = 0;
267         for(int i = 1; i <= n; i++)
268             sum += (p[i] = read()), r[i] = read(), d[i] = read();
269         for(int i = 1; i <= m; i++)
270             ss[i] = read();
271         ss[m + 1] = 0;
272         int tmp = ss[1];
273         sort(ss + 1, ss + 1 + m, greater<int>());
274         for(int i = 1; i <= m; i++)
275             ss[i] -= ss[i + 1];
276         double l = 0, r = 1.0 * sum / tmp, mid;
277         while(fabs(r - l) > eps){
278             mid = (l + r) / 2.0;
279             if(check(mid)) r = mid;
280             else l = mid;
281         }
282         printf("%.5lf\n", mid);
283     }
284     Main_Init();
285     return 0;
286 }

 

推荐阅读