• 「CF1119G」Get Ready for the Battle


    题目

    点这里看题目。

    分析

    3100 的构造题,已经很奇妙了......

    题目要求我们先输出所需的最少轮数。这一部分其实具有一定的提示性,因为我们可以猜一个下界,然后看是否能够达到它。比如,一个很好猜的下界就是 \(t_{\min}=\lceil\frac{\sum hp}{n}\rceil\)

    但事实是,如果没有很好的构造技巧,即使知道这个下界可以被构造出来也没有用。

    我们可以先对这个下界进行一些简单的分析。我们称给令敌方总血量减少 \(n\) 的一步为“完美”的一步。如果要达到这个下界,就说明我们在前 \(t_{\min}-1\) 步中,每一步都必须是“完美”的。当 \(n\le m\) 的时候,这个要求很简单,我们只需要将每个组的大小都设置为 1 即可。

    考虑 \(n>m\) 的时候。我们不妨再进行一些限制,例如我们要顺序攻击每一组敌军,并且要求在攻击前 \(m-1\) 组时,每一步都是“完美”的。此时,我们可以将\(m\) 组合并为一整组。那么前 \(m-1\) 组的每一条组间分界线必须和我方的某一条攻击组分界线重合。

    也即,设 \(s_i=\sum_{k=1}^ihp_k\),则对于所有的 \(1\le i<m\),我们的构造攻击方案都满足存在一个攻击的前缀,它带来的伤害之和为 \(s_i\)。由于每一轮攻击伤害之和都是 \(n\),因此我们构造的分组方案中,需要存在一个和为 \(s_i\bmod n\) 的前缀。

    注意到需要满足的前缀只有 \(m-1\) 个,因此我们可以对 \(s_i\bmod n\) 排序后,取它们的差分,将剩下的再单独分成一组。最后扫描敌军的血量并分配即可。

    小结:

    1. 在有步数限制的构造中,猜一个下界、上界可以让我们构造的目标清晰很多,也可以明确方向,降低构造难度。

    2. 注重基础分析,转换观察的角度。如果想不到“前面很多步都必须是‘完美’的”这个基础结论,那么后面的问题就很困难。同时,如果没有将若干组合并成为一组来观察,那也不简单了。

    代码

    #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 MAXN = 1e6 + 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;
    }
    
    int hp[MAXN];
    
    int N, M;
    
    namespace SmallN {
        int seq[MAXN], tot;
    
        void Solve() {
            rep( i, 1, N ) putchar( '1' ), putchar( ' ' );
            rep( i, N + 1, M ) putchar( '0' ), putchar( ' ' );
            puts( "" );
            rep( i, 1, M ) {
                int tims = hp[i] / N;
                rep( j, 1, tims ) {
                    rep( k, 1, M )
                        write( i ), putchar( ' ' );
                    puts( "" );
                }
                hp[i] %= N;
                rep( j, 1, hp[i] )
                    seq[++ tot] = i;
            }
            for( int i = 1 ; i <= tot ; i += N ) {
                int lim = MIN( N, tot - i + 1 );
                rep( j, 1, lim ) write( seq[i + j - 1] ), putchar( ' ' );
                rep( j, lim + 1, M ) putchar( '1' ), putchar( ' ' );
                puts( "" );
            }
        }
    }
    
    namespace LargeN {
        int pref[MAXN], val[MAXN], tmp[MAXN], pos[MAXN], tot;  
    
        void Solve() {
            rep( i, 1, M ) pref[i] = pref[i - 1] + hp[i];
            rep( i, 1, M - 1 ) tmp[++ tot] = pref[i] % N;
            std :: sort( tmp + 1, tmp + 1 + tot );
            tot = std :: unique( tmp + 1, tmp + 1 + tot ) - tmp - 1;
            rep( i, 1, tot ) { 
                val[i] = tmp[i] - tmp[i - 1];
                pos[tmp[i]] = i;
            }
            val[tot + 1] = N - tmp[tot];
            rep( i, 1, M ) write( val[i] ), putchar( i == M ? '\n' : ' ' );
            for( int i = 1, j ; i < M ; ) {
                for( ; i < M && ! hp[i] ; i ++ );
                if( i == M ) break;
                int su = 0, lst = 0;
                for( j = i ; j < M && ( su += hp[j] ) <= N ; j ++ );
                for( int k = i ; k <= j ; k ++ ) {
                    while( lst < M && hp[k] >= val[lst + 1] ) {
                        hp[k] -= val[++ lst];
                        write( k ), putchar( ' ' );
                    }
                    if( lst == M ) break;
                }
                for( int i = lst + 1 ; i <= M ; i ++ )
                    hp[M] -= val[i], write( M ), putchar( ' ' );
                puts( "" );
            }
            if( hp[M] > 0 ) {
                int tims = ( hp[M] + N - 1 ) / N;
                rep( j, 1, tims ) {
                    rep( k, 1, M )
                        write( M ), putchar( ' ' );
                    puts( "" );
                }
            }
        }
    }
    
    int main() {
        int su = 0;
        read( N ), read( M );
        rep( i, 1, M ) read( hp[i] ), su += hp[i];
        printf( "%d\n", ( su + N - 1 ) / N );
        if( N <= M ) 
            SmallN :: Solve();
        else 
            LargeN :: Solve();
        return 0;
    }
    
  • 相关阅读:
    Python中,如何使用 IPython 调试(debug)程序
    【转】Byte[ ]和Base64之间的转换(加密)
    odoo理解domain和context使用场景
    odoo基本的模型(models)理解
    利令智昏
    只有经历了黑暗
    被未知恐惧战胜
    战争的三个阶段
    高贵的人民公仆
    搭建萤火商城2.0 杭州爱软测
  • 原文地址:https://www.cnblogs.com/crashed/p/15556987.html
Copyright © 2020-2023  润新知