【题解】ZJOI2010贪吃的老鼠

%%%%真的好强...看题解我都看了好久才完全明白。放一下参考的博客,谢谢神犇QAQ

1号博客     2号博客(超级赞的啦)

 因为理解的过程太艰辛,所以必须记录一下这道强题:这道题目最难的两个约束就在于:保证一个时间一只老鼠只吃一块奶酪,一个时间一块奶酪只被一只老鼠吃。第一个想法还是相对明显的:二分答案,离散化时间节点(一个节点代表此节点到上一节点之间的时间段)。

 最妙的一处是老鼠的拆点。在我们之前离散出来的时间段中,每一段都维护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
", 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;
}