【题解】ZJOI2010贪吃的老鼠
%%%%真的好强...看题解我都看了好久才完全明白。放一下参考的博客,谢谢神犇QAQ
因为理解的过程太艰辛,所以必须记录一下这道强题:这道题目最难的两个约束就在于:保证一个时间一只老鼠只吃一块奶酪,一个时间一块奶酪只被一只老鼠吃。第一个想法还是相对明显的:二分答案,离散化时间节点(一个节点代表此节点到上一节点之间的时间段)。
最妙的一处是老鼠的拆点。在我们之前离散出来的时间段中,每一段都维护m个老鼠的速度差值。我也不知道原作者是怎么想到建图方式的,所以无法解释思路的走向过程,只能说明一下建图的方式&为什么这样是对的。举个例子:我们现在有三只老鼠,速度分别是:5, 4, 3, 那么我们就可以认为是有3只速度为3(3 - 0 = 3)的老鼠,1只速度为1(4 - 3 = 1)的老鼠,还有一只速度为1(5 - 4 = 1)的老鼠。(为什么后面这两只不一样在后面会解释)。暂且将几只记为id,速度记为v。
s-->每一块奶酪,流量为p[i]; 每一块奶酪-->这个时间段内的老鼠拆点,流量为老鼠拆点v*t(时间); 每一个老鼠拆点-->t,流量为id*v*t。现在让我们来看一下这张图是如何完美的满足了此题的1、2两条约束的:1.保证一个时间一只老鼠只吃一块奶酪:老鼠是按时间段拆的点,连向t,前面所说的拆为了3,1,1的情况,三条都流满说明三只老鼠都在吃,分别流量为1表明速度为3的老鼠在吃...这一点应该还是能够理解的,因为一一对应拆点,必然满足约束。2.保证一个时间一块奶酪只被一只老鼠吃:每一块奶酪都向不同的时间段的拆点通了流量,注意这里的流量并没有乘上id,所以都等于1*v*t,也就满足了一只老鼠的约束。
以下是代码:(没有看懂的只要手抄一次代码,想必就能明白啦!)
#include <bits/stdc++.h> using namespace std; #define maxn 100000 #define eps 0.0000001 #define INF 99999999.00 #define db double int n, m, s, t, cnp, head[maxn], lev[maxn], cur[maxn]; db R, L, tot, p[maxn], r[maxn], d[maxn], T[maxn], v[maxn]; struct edge { int to, last; db c; }E[maxn]; bool cmp(db a, db b) { return a > b; } void add(int u, int v, db c) { E[cnp].to = v, E[cnp].last = head[u], E[cnp].c = c; head[u] = cnp ++; E[cnp].to = u, E[cnp].last = head[v], E[cnp].c = 0; head[v] = cnp ++; } bool bfs() { queue <int> q; memset(lev, 0, sizeof(lev)); q.push(s); lev[s] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = E[i].last) { int v = E[i].to; if(!lev[v] && E[i].c > eps) { lev[v] = lev[u] + 1; if(lev[t]) return true; q.push(v); } } } return false; } db dfs(int u, db nf) { if(u == t || nf < eps) return nf; bool done = false; db ff = 0.0; for(int i = cur[u]; i != -1; i = E[i].last) { int v = E[i].to; if(!nf) break; if(E[i].c > eps && lev[v] == lev[u] + 1) { done = true; db af = dfs(v, min(E[i].c, nf)); cur[u] = i; nf -= af, ff += af; E[i].c -= af, E[i ^ 1].c += af; } } if(!done) lev[u] = -1; return ff; } db dinic() { db flow = 0.0; while(bfs()) { memcpy(cur, head, sizeof(head)); flow += dfs(s, INF); } return flow; } void init() { cnp = 0; memset(head, -1, sizeof(head)); } void solve() { db sum = tot; s = 0, t = 2 * m * n + n + 1; int cnt = 0; while(R - L > eps) { db mid = (R + L) / 2.0; init(); for(int i = 1; i <= n; i ++) { add(0, i, p[i]); T[2 * i - 1] = r[i], T[2 * i] = d[i] + mid; } sort(T + 1, T + 2 * n + 1), cnt = n; for(int i = 1; i <= m; i ++) { for(int j = 2; j <= 2 * n; j ++) { if(T[j] - T[j - 1] < eps) continue; ++ cnt; add(cnt, t, i * v[i] * (T[j] - T[j - 1])); for(int k = 1; k <= n; k ++) { if(r[k] - T[j - 1] < eps && (d[k] + mid - T[j] > -eps)) add(k, cnt, v[i] * (T[j] - T[j - 1])); } } } db maxflow = dinic(); if(sum - maxflow < eps) R = mid; else L = mid; } printf("%lf\n", L); } int main() { int T; scanf("%d", &T); while(T --) { tot = 0.0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { scanf("%lf%lf%lf", &p[i], &r[i], &d[i]); tot += p[i]; } for(int i = 1; i <= m; i ++) scanf("%lf", &v[i]); sort(v + 1, v + 1 + m, cmp); R = (db) tot / v[1] + 1.0, L = 0.0; for(int i = 1; i < m; i ++) v[i] -= v[i + 1]; solve(); } return 0; }