[ZJOI2010]贪吃的老鼠 网络流

~~~题面~~~

题解:

这是一道强题emmmm,做法非常巧妙,,,我也是看了好久大佬题解才看明白一点

首先考虑没有限制的情况,即n个老鼠可以在同一时刻吃同一块奶酪

对各个时间段拆点,连奶酪 ---> 老鼠(反过来也是一样的,只不过不方便),相连的奶酪要符合时间段的限制,

相当于把老鼠拆成很多个小时刻,连向这个时刻它可以吃的奶酪,流量为它在这段时间内可以吃的奶酪总量,

限流可以在汇点到老鼠的路径上进行。

但这个并不能满足同一时刻一块奶酪只能被一个老鼠吃这个条件,因此我们对老鼠再拆点,

把每个老鼠拆成的小时刻再根据速度差分,

比如8 4 2 1,四只老鼠。差分后就是:

8 - 4 = 4;

4 - 2 = 2;

2 - 1 = 1;

1 - 1 = 1;

然后按照编号来定权值

流量就为编号 * 速度(差分后) * 时间;

为什么这样?

8 = 4 + 2 + 1 + 1

4 = 2 + 1 + 1

2 = 1 + 1

1 = 1

可以很明显看到这是一个三角形,且每层都是相同的数字,对应到我们差分数组,对于每个差分后的速度,刚好有编号个,

这样就可以保证总的流量合法了。

那为什么这样可以保证同一时刻只有一只老鼠呢?

可以这样感性的理解:

注意到任意一只老鼠都可以由差分数组凑出,那么不管网络流怎样跑出答案,我们都可以通过分解一下流量,加加减减之类的数学方法凑成这几只老鼠,因此是合法的。

也就是说网络流跑出的东西也许跟原图不一样,但它可以用来判断是否合法(满流即合法),这就够了。

因此我们二分答案,每次都重新建图,跑最大流,满流为true,else 为 false。

代码有点长(打的isap),改成dinic应该会短很多

(数组开这么小是卡常后的结果,,,,)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 5000
  5 #define ac 20000
  6 #define eps 1e-6
  7 int x, n, m, T, cnt, tmp, ss, tt;
  8 int p[AC], v[AC], last[AC];
  9 double all, ll, rr, mid, ans, addflow;
 10 double r[AC], d[AC], haveflow[ac], t[AC];
 11 int Head[AC], Next[ac], date[ac], tot;
 12 int good[AC], have[AC], c[AC];
 13 int q[AC], tail, head;
 14 /*神奇的网络流,,,,
 15 对每个时间点进行离散化*/
 16 inline int read()
 17 {
 18     int x = 0;char c = getchar();
 19     while(c > '9' || c < '0') c = getchar();
 20     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 21     return x;
 22 }
 23 
 24 inline void upmin(double &a, double b)
 25 {
 26     if(b < a) a = b;
 27 }
 28 
 29 inline void add(int f, int w, double S)
 30 {
 31     date[++tot] = w, Next[tot] = Head[f], haveflow[tot] = S, Head[f] = tot;
 32     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
 33     //printf("%d ---> %d %.4lf
",f,w,S);
 34 }
 35 
 36 bool bfs()
 37 {
 38     int x, now;
 39     head = tail = 0;
 40     c[tt] = 1, have[1] = 1, q[++tail] = tt;
 41     while(head < tail)
 42     {
 43         x = q[++head];
 44         for(R i = Head[x]; i ; i = Next[i])
 45         {
 46             now = date[i];
 47             if(haveflow[i ^ 1] && !c[now])
 48             {
 49                 c[now] = c[x] + 1;
 50                 q[++tail] = now; 
 51                 ++have[c[now]];
 52             }
 53         }    
 54     }
 55     memcpy(good, Head, sizeof(Head));
 56     return c[ss];
 57 }
 58 
 59 void aru()
 60 {
 61     while(x != ss)
 62     {
 63         haveflow[last[x]] -= addflow;
 64         haveflow[last[x] ^ 1] += addflow;
 65         x = date[last[x] ^ 1];
 66     }
 67     ans += addflow;
 68 }
 69 
 70 double isap()
 71 {
 72     int now; bool done;
 73     x = ss, addflow = INT_MAX;
 74     while(c[ss] != ac + 1)
 75     {
 76         if(x == tt) aru(), addflow = INT_MAX;
 77         done=false;
 78         for(R i = good[x]; i ; i = Next[i])
 79         {
 80             now = date[i];
 81             if(c[now] == c[x] - 1 && haveflow[i])
 82             {
 83                 last[now] = i;
 84                 upmin(addflow, haveflow[i]);
 85                 good[x] = i;
 86                 done = true;
 87                 x = now;
 88             }
 89         }
 90         if(!done)
 91         {
 92             int go = ac;
 93             for(R i = Head[x]; i ; i = Next[i])
 94             {
 95                 now = date[i];
 96                 if(haveflow[i] && c[now]) go = c[now]; 
 97             }
 98             if(!(--have[c[x]])) break;
 99             ++have[c[x] = go + 1];
100             good[x] = Head[x];
101             if(x != ss) x = date[last[x] ^ 1];//这是回到上一个节点啊  
102         }
103     }    
104     return ans;
105 }
106 
107 inline bool cmp(double a, double b)
108 {
109     return a > b;
110 }
111 
112 void pre()
113 {
114     tot = 1, all = 0;
115     n = read() ,m = read();
116     for(R i = 1; i <= n; i++)
117     {
118         p[i] = read(), r[i] = read(), d[i] = read();
119         all += (double)p[i];
120     }
121     for(R i = 1; i <= m; i++) v[i] = read();
122     sort(v + 1, v + m + 1, cmp);//error!!!老鼠是m只!!!!不是n只!!!
123     rr = (double) all / (double)v[1] + 1.0, ll = 0;
124     for(R i = 1; i < m; i++) v[i] -= v[i + 1];//对速度差分
125 }       
126 
127 void build()
128 {
129     tot = 1, ans = 0;
130     memset(Head, 0, sizeof(Head));//应该要放这里重置吧
131     memset(have, 0, sizeof(have));
132     memset(c, 0, sizeof(c));
133     for(R i = 1; i <= n; i++)
134     {
135         add(ss, i, p[i]);
136         t[2 * i - 1] = r[i], t[2 * i] = d[i] + mid;//为离散化做准备
137     }
138     sort(t + 1, t + 2 * n + 1);//准备离散化了
139     cnt =  0, tmp = n;//因为不能和前n个奶酪的编号重了 
140     int a = 2 * n;
141     t[a + 1] = INT_MAX;//不然最后一个点进不来
142     for(R i = 1; i <= a; i++)
143         if(t[i + 1] - t[i] > eps) t[++cnt] = t[i];//去重 
144     for(R i = 1; i <= m; i++)//枚举老鼠
145     {
146         for(R j = 2; j <= cnt; j++)//因为要两个时间点才组成一个时间段
147         {
148             ++tmp;
149             add(tmp, tt, i * v[i] * (t[j] - t[j - 1]));//连离散化的老鼠到汇点
150             for(R k = 1; k <= n; k++)//枚举奶酪
151             {
152                 if(r[k] - t[j-1] < eps && (d[k] + mid - t[j] > - eps))
153                     add(k, tmp, v[i] * (t[j] - t[j - 1]));//连奶酪向老鼠
154             }//r可以小于t(早就开始了),所以负数也合法,后面是同理的,只是移项了tarjan123
155             
156         }
157     }
158 }
159 
160 void half()
161 {
162     ss = 2 * m * n + n + 1, tt = ss + 1;//error!!!ss是要2 * m * n + n + 1啊
163     while(rr - ll > eps)
164     {
165         mid = (rr + ll) / 2.0;
166         build();
167         if(bfs() && all - isap() < eps) rr = mid;
168         else ll = mid;
169         //printf("%.4lf
",ans);
170         //printf("%.4lf %.4lf

",ll,rr);
171     }
172     printf("%lf
", ll);
173 }
174 
175 void work()
176 {
177     T=read();
178     while(T--)
179     {
180         pre();
181         half();
182     }
183 
184 }
185 
186 int main()
187 {
188 //    freopen("in.in","r",stdin);
189     //freopen("cheese.out","w",stdout);
190     work();
191 //    fclose(stdin);
192     //fclose(stdout);
193     return 0;
194 }
原文地址:https://www.cnblogs.com/ww3113306/p/9175976.html