1. 程式人生 > 其它 >「ZJOI2010」貪吃的老鼠

「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;
}