[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\n",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\n",ans); 170 //printf("%.4lf %.4lf\n\n",ll,rr); 171 } 172 printf("%lf\n", 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 }