「CEOI2010」MP3 Player

题目

点这里看题目。

分析

首先需要弄清楚如何枚举 \(t\)。由于无论按键是否有效,播放器都会被重置状态。因此,某个按键是否有效仅仅取决于上一个按键与此的时间差和 \(t\) 的关系。那么我们就可以很好地用相邻差来划分 \(t\) 的阶段——有效的 \(t\) 的阶段只有 \(O(n)\) 个。枚举 \(t\) 并模拟。当我们求出 \(t_{\max}\) 之后,剩下的 \(V_2\) 是关于 \(V_1\) 的单调不降的函数,直接二分就好了,复杂度为 \(O(n^2)\)

考虑加速枚举 \(t\) 的过程。实际上在某个确定的阶段中,我们仅仅想要知道是否存在 \(V_1\),使得最终计算出来的结果为 \(V_2\)——或者,我们可以构造一个函数 \(f_t(V_1)\),而我们只需要考察 \(V_2\) 是否在 \(V_1\) 之中。

没有操作的情况是 \(f(V_1)=V_1\),而有一个 + 的操作是 \(f(V_1)=\min\{V_{\max},V_1+1\}\),有一个 - 的操作是 \(f(V_1)=\max\{0,V_1-1\}\)。多次操作则是这样的基本函数的复合,而更一般的情况是,我们发现 \(f_t(V_1)\) 总可以被描述为 \(\min\{\max\{x+c,a\},b\}\),它的复合也可以比较容易地 \(O(1)\) 计算。

因此,我们的问题变成了——动态维护 \(O(n)\) 个函数的复合结果,并且需要支持将某一个函数重置为 \(f(V_1)=V_1\)。由于结合律,这是一个比较简单的、可以使用线段树维护的问题。总复杂度即为 \(O(n\log n)\)

小结:

  1. 能做题的前提是能把题读懂。
  2. 能够得到这样一个思路,主要是因为我们可以发现 \(f(V_1)\) 应当是连续的整数区间,进一步地发现 \(f(V_1)\) 的简单形式。
  3. 熟悉函数复合的形式,熟悉 \(\min,\max\) 的运算法则

代码

#include <cstdio>
#include <utility>
#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 MAXN = 2e5 + 5;

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

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

int Vmax;

struct Func {
    int a, b, c;

    Func(): a( 0 ), b( Vmax ), c( 0 ) {}
    Func( int A, int B, int C ): a( A ), b( B ), c( C ) {} 

    inline int  operator () ( const int  &x ) const { return MIN( MAX( x + c, a ), b ); } 

    inline Func operator [] ( const Func &g ) const {
        return Func( ( * this ) ( g.a ), ( * this ) ( g.b ), c + g.c );
    }
};

typedef std :: pair<int, int> Event;

Event eve[MAXN];
int tot = 0;

Func tre[MAXN << 2];

int tim[MAXN];
bool swt[MAXN];

int N, V2;

void Build( const int x, const int l, const int r ) {
    if( l == r ) {
        if( swt[l] )
            tre[x] = Func( 0, Vmax, -1 );
        else tre[x] = Func( 0, Vmax, 1 );
        return ;
    }
    int mid = ( l + r ) >> 1;
    Build( x << 1, l, mid );
    Build( x << 1 | 1, mid + 1, r );
    tre[x] = tre[x << 1 | 1][tre[x << 1]];
}

void Reset( const int x, const int l, const int r, const int p ) {
    if( l == r ) { tre[x] = Func(); return ; }
    int mid = ( l + r ) >> 1;
    if( p <= mid ) Reset( x << 1, l, mid, p );
    else Reset( x << 1 | 1, mid + 1, r, p );
    tre[x] = tre[x << 1 | 1][tre[x << 1]];
}

int Solve() {
    int l = 0, r = Vmax, mid;
    while( l < r ) {
        mid = ( l + r + 1 ) >> 1;
        if( tre[1]( mid ) <= V2 ) l = mid;
        else r = mid - 1;
    }
    return l;
}

signed main() {
    read( N ), read( Vmax ), read( V2 );
    rep( i, 1, N ) {
        char op[5];
        scanf( "%s", op ), read( tim[i] );
        swt[i] = op[0] == '-';
    }
    Build( 1, 2, N );
    if( tre[1]( 0 ) <= V2 && V2 <= tre[1]( Vmax ) )
        return puts( "infinity" ), 0;
    rep( i, 1, N - 1 ) eve[++ tot] = Event( tim[i + 1] - tim[i], i + 1 );
    std :: sort( eve + 1, eve + 1 + tot,
        [] ( const Event &a, const Event &b ) -> bool {
            return a.first > b.first;
        } );
    for( int i = 1, j ; i <= tot ; i = j ) {
        for( j = i ; j <= tot && eve[j].first == eve[i].first ; )
            Reset( 1, 2, N, eve[j ++].second );
        if( tre[1]( 0 ) <= V2 && V2 <= tre[1]( Vmax ) ) {
            write( eve[i].first - 1 ), putchar( ' ' );
            write( Solve() ), putchar( '\n' );
            break;
        }
    }
    return 0;
}
posted @ 2021-12-30 22:57  crashed  阅读(53)  评论(0编辑  收藏  举报