「ZJOI2010」贪吃的老鼠

题目

点这里看题目。

分析

注意到,这个题目的特点之一是时间是连续的。这意味着我们可以单纯地关注数值而不太需要关心具体的时间。比如,一只老鼠在长度为 \(t\) 的时间段内,想要吃下 \(x_k\) 的第 \(k\) 块奶酪,则这一点能否做到,完全等价于 \(\sum_kx_k\) 是否 \(\le ts\)

想了好久如何转化将时间和奶酪互相转化,没想到被题解直接一下子解决了

不过老鼠能吃哪些奶酪还是需要考虑的问题。这很简单,直接将时间离散成时间段即可。每一个时间段内老鼠能吃的奶酪是不会变的。在一个确定的时间段内,我们可以直接将老鼠作为网络流的结点来建图吗?似乎不可以,我们必须保证一个奶酪被一只老鼠吃,但这样会导致奶酪选中一个老鼠的子集——这似乎也是性质之一,怎么用呢?

如果苛刻一点,我们可能会要求一种拆分方法,使得其中任何一个子集选出来都可以恰好对应到一只老鼠。显然这是不可能的,这必然要求 \(n\)\(2\) 的一个整幂。不过,我们可以退而求其次,利用实数流很容易进行调整的性质,我们也许只需要保证老鼠可以被对应到某些子集即可?这是易于实现的,差分老鼠的速度即可。

进一步地,差分之后,我们可以建立从奶酪指向老鼠的边。如何确定老鼠的“容量”?差分之后,第 \(k\) 快的老鼠的差分值实际上会被算到 \(m-k+1\) 次,将这个系数算到容量里面即可。

至于这个东西怎么满足题目要求的,请参考他人博客

小结:

  1. 注意某些变量的特点,这里的连续时间就是一个很强的条件。

    注意到了反而不会用

  2. 差分想法很重要,但怎么想到差分更重要。在这里,主要是子集选取这样一个结构给了我们启发。

代码

#include <cmath>
#include <cstdio>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int inf = 1e9;
const double eps = 1e-7, INF = 1e18;
const int MAXN = 1e5 + 5, MAXE = 1e6 + 5, MAXn = 100;

template<typename _T>
void read( _T &x ) {
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = -x;
}

template<typename _T>
void write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T>
_T Min( const _T a, const _T b ) {
    return a < b ? a : b;
}

struct Edge {
    int to, nxt;
    double c;
} Graph[MAXE << 1];

int q[MAXN];
int dep[MAXN], cur[MAXN];
int head[MAXN], cnt = 1, ntot = 0;

double tim[MAXn]; int tot = 0;
int P[MAXn], R[MAXn], D[MAXn], S[MAXn];

int N, M;

inline int Sgn( const double &x ) {
    return fabs( x ) < eps ? 0 : ( x < 0 ? -1 : 1 );
}

inline void AddEdge( const int from, const int to, const double C ) {
    Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    Graph[cnt].c = C, head[from] = cnt;
}

inline void AddE( const int from, const int to, const double C ) {
    AddEdge( from, to, C ), AddEdge( to, from, 0 );
}

bool BFS( const int S, const int T ) {
    int h = 1, t = 0;
    rep( i, 1, ntot ) dep[i] = inf;
    dep[q[++ t] = S] = 0;
    while( h <= t ) {
        int u = q[h ++];
        for( int i = head[u], v ; i ; i = Graph[i].nxt )
            if( Sgn( Graph[i].c ) && dep[v = Graph[i].to] > dep[u] + 1 )
                dep[q[++ t] = v] = dep[u] + 1;
    }
    return dep[T] < inf;
}

double DFS( const int u, const double lin, const int T ) {
    if( u == T ) return lin;
    int v; double used = 0, ret, c;
    for( int &i = cur[u] ; i ; i = Graph[i].nxt ) {
        v = Graph[i].to, c = Graph[i].c;
        if( Sgn( c ) && dep[v] == dep[u] + 1 && 
            Sgn( ret = DFS( v, Min( lin - used, c ), T ) ) ) {
            used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
            if( ! Sgn( used - lin ) ) break;
        }
    }
    if( Sgn( used - lin ) ) dep[u] = inf;
    return used;
}

double Dinic( const int S, const int T ) {
    double flow = 0;
    while( BFS( S, T ) ) {
        rep( i, 1, ntot ) cur[i] = head[i];
        flow += DFS( S, INF, T );
    }
    return flow;
}

#define ID( t, x ) ( ( (t) - 1 ) * M + (x) + N )

bool Chk( const double T ) {
    cnt = 1, tot = 0;
    rep( i, 1, N ) {
        tim[++ tot] = R[i];
        tim[++ tot] = D[i] + T;
    }
    std :: sort( tim + 1, tim + 1 + tot );
    tot = std :: unique( tim + 1, tim + 1 + tot ) - tim - 1;
    ntot = N + M * ( tot - 1 );
    const int s = ++ ntot, t = ++ ntot;
    rep( i, 1, ntot ) head[i] = 0;
    rep( i, 1, N ) {
        AddE( s, i, P[i] );
        int l = std :: lower_bound( tim + 1, tim + 1 + tot, R[i] ) - tim,
            r = std :: lower_bound( tim + 1, tim + 1 + tot, D[i] + T ) - tim;
        rep( j, l, r - 1 ) rep( k, 1, M )
            AddE( i, ID( j, k ), ( tim[j + 1] - tim[j] ) * S[k] );
    }
    rep( j, 1, tot - 1 ) rep( k, 1, M )
        AddE( ID( j, k ), t, ( tim[j + 1] - tim[j] ) * S[k] * ( M - k + 1 ) );
    double su = 0;
    rep( i, 1, N ) su += P[i];
    return ! Sgn( su - Dinic( s, t ) );
}

int main() {
    int T;
    for( read( T ) ; T -- ; ) {
        read( N ), read( M );
        rep( i, 1, N ) read( P[i] ), read( R[i] ), read( D[i] );
        rep( i, 1, M ) read( S[i] );
        std :: sort( S + 1, S + 1 + M );
        per( i, M, 1 ) S[i] -= S[i - 1];
        double l = 0, r = 1e9, mid;
        while( r - l > 1e-7 ) {
            mid = ( l + r ) / 2;
            if( Chk( mid ) ) r = mid;
            else l = mid;
        }
        printf( "%g\n", l );
    }
    return 0;
}
posted @ 2022-03-16 22:19  crashed  阅读(58)  评论(0编辑  收藏  举报