zoukankan      html  css  js  c++  java
  • 「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;
    }
    
  • 相关阅读:
    Android开发中适配多种 ROM 的快捷方式是如何实现的?
    Android开发利用Volley框架下载和缓存网络图片
    Android调用系统相机、自定义相机、处理大图片
    Excel中MATCH函数的正确使用
    Excel中INDEX函数的使用
    (转)Delphi2009初体验
    利用NSA的MS17-010漏洞利用工具实现Win 7和Win Server 2008系统入侵
    delphi 的结构体对齐关键字
    KVM虚拟化(一)—— 介绍与简单使用
    kvm虚拟化学习笔记(四)之kvm虚拟机日常管理与配置
  • 原文地址:https://www.cnblogs.com/crashed/p/15750940.html
Copyright © 2011-2022 走看看