zoukankan      html  css  js  c++  java
  • 「CF1442D」Sum

    题目

    点这里看题目。

    分析

    很容易想到一个 \(O(nk^2)\) 的暴力 DP,当然也很容易看出这个 DP 根本没有和“单调不降”扯上任何关系。因此,接下来我们要做的就是利用好“单调不降”的性质。

    一个想法是凸性——数组前缀和是下凸的。不过,由于我们对下凸壳做的是 \(\max\) 卷积,所以这样的凸性暂时还没有什么用。

    另一个想法是化归方案,找出方案的某些性质。由于“单调不降”,选越多越“划算”,我们有这样的直觉——可能很多数组都是被完全选中了的,只有少数数组仅有一个前缀被选中

    接下来,我们需要抽象地研究一下。不妨假设有两个数组 \(i,j\) 都仅有一个非空真前缀被选中,一个被选了 \(l_i\) 个,另一个被选了 \(l_j\) 个。根据题目条件,有 \(a_{i,l_i}\le a_{i,l_{i}+1}\)\(a_{j,l_j}\le a_{j,l_j+1}\)。在这样的背景之下,我们还可以假设 \(a_{i,l_i}\le a_{j,l_j+1}\)。由于题目单调性的保证,我们还可以知道 \(a_{i,l_i-1}\le a_{j,l_j+2},a_{i,l_i-2}\le a_{j,l_j+3}\dots\)

    现在,我们就可以将 \(a_i\) 中被选的元素调整\(a_j\) 中去,得到一个不劣的方案,直到 \(a_i\) 中一个都没有被选或 \(a_j\) 被完全选中。因此,我们可以知道,一定有一种最优解,满足仅有至多一个数组,它仅有一个非空真前缀被选中

    我们现在可以枚举特殊的一个数组,剩下的就是 01 背包问题。如果暴力做 01 背包仍然会不幸地得到 \(O(nk^2)\) 的算法。一个在多点运算中常用的技巧是,进行自底向上或自顶向下的分治。最终我们可以得到一个 \(O(nk\log n)\) 的算法。

    小结:

    1. 注意对问题的感知
    2. 灵活运用调整的思想,分析有效方案的特点,缩减需要搜索的范围;
    3. 注意一下分治方法的应用。

    代码

    #include <cstdio>
    #include <vector>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const LL INF = 1e18;
    const int MAXN = 3005;
    
    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 MAX( const _T a, const _T b ) {
        return a > b ? a : b;
    }
    
    std :: vector<LL> q[MAXN];
    
    LL dp[MAXN], buc[20][MAXN];
    
    LL su[MAXN];
    int siz[MAXN];
    
    LL ans;
    int N, K;
    
    #define ID( l, r ) ( ( (l) + (r) ) | ( (l) != (r) ) )
    
    void Divide( const int l, const int r, const int dep ) {
        if( l == r ) {
            for( int i = 0 ; i <= siz[l] && i <= K ; i ++ )
                ans = MAX( ans, q[l][i] + dp[K - i] );
            return ;
        }
        int mid = ( l + r ) >> 1;
        rep( j, 0, K ) buc[dep][j] = dp[j];
        rep( j, mid + 1, r )
            per( k, K, siz[j] )
                dp[k] = MAX( dp[k], dp[k - siz[j]] + su[j] );
        Divide( l, mid, dep + 1 );
        rep( j, 0, K ) dp[j] = buc[dep][j];
        rep( j, l, mid )
            per( k, K, siz[j] )
                dp[k] = MAX( dp[k], dp[k - siz[j]] + su[j] );
        Divide( mid + 1, r, dep + 1 );
        rep( j, 0, K ) dp[j] = buc[dep][j];
    }
    
    int main() {
        read( N ), read( K );
        rep( i, 1, N ) {
            LL pref = 0;
            read( siz[i] );
            q[i].push_back( 0 );
            rep( j, 1, siz[i] ) {
                int x; read( x );
                q[i].push_back( pref += x );
            }
            su[i] = pref;
        }
        ans = - INF;
        rep( j, 0, K ) dp[j] = - INF;
        dp[0] = 0;
        Divide( 1, N, 0 );
        write( ans ), putchar( '\n' );
        return 0;
    }
    
  • 相关阅读:
    clone对象
    Windows下swoole扩展的编译安装部署
    PHP备忘录
    Linux文档的压缩与打包
    Linux系统启动过程
    Linux虚拟机安装
    MongoDB自动增长
    MongoDB固定集合(Capped Collections)
    Activating Google Cloud Storage
    Downloading the Google Cloud Storage Client Library
  • 原文地址:https://www.cnblogs.com/crashed/p/15552128.html
Copyright © 2011-2022 走看看