[CF1372E]Omkar and Last Floor

题目

点这里看题目。

分析

好妙的题!

初看起来无从下手(我最初一直想着要分行依次叠加贡献),这样的话,我们不妨来看一下,第一步应该怎么计算贡献。

面对区间 \([1,m]\) ,一种方法是首先选出一列 \(k\),然后最大化这一列上的和——显然就是 \(n\) 。接着,所有经过了 \(k\) 的区间都不能再用了。那么问题自然地被划分成了 \([1,k-1]\)\([k+1,m]\) 两个独立的子问题。

这提醒了我们,可以使用区间 DP 。可以考虑定义状态为:

\(f(i,j)\) :左右端点都在 \([i,j]\) 内的区间的最大贡献和。

转移比较显然。记 \(S(i,j,k)\) 为所有的左右端点都在 \([i,j]\) 中,且经过了 \(k\) 这个位置的区间数量。那么就有转移:

\[f(i,j)=\max\{f(i,k-1)+f(k+1,j)+(S(i,j,k))^2|i\le k\le j\} \]

这里的 \(S\) 理论上来说,可以直接 \(O(n^3)\) 地预处理出来(高位前缀和)。实际上直接 \(O(n^4)\) 也无伤大雅。

总时间复杂度就是 \(O(n^3)\sim O(n^4)\)

小结:

本题的突破口就在于最初对于贡献计算的思考,所以如果最初没有思路,就可以先对问题进行一些基础的分析。

代码

#include <cstdio>

const int INF = 0x3f3f3f3f;
const int MAXN = 105;

template<typename _T>
void read( _T &x )
{
    x = 0; char s = getchar(); int f = 1;
    while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); }
    x *= f;
}

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

int f[MAXN][MAXN];
int su[MAXN][MAXN][MAXN];

int N, M;

int main()
{
    read( N ), read( M );
    for( int i = 1 ; i <= N ; i ++ )
    {
        int K, l, r;
        read( K );
        while( K -- )
        {
            read( l ), read( r );
            for( int a = 1 ; a <= l ; a ++ )
                for( int b = r ; b <= M ; b ++ )
                    su[a][b][l] ++, su[a][b][r + 1] --;
        }
    }
    for( int i = 1 ; i <= M ; i ++ )
        for( int j = i + 1 ; j <= M ; j ++ )
            for( int k = i ; k <= j ; k ++ )
                su[i][j][k] += su[i][j][k - 1];
    for( int i = M ; i ; i -- )
    {
        f[i][i + 1] = -INF;
        for( int j = i ; j <= M ; j ++ )
            for( int k = i ; k <= j ; k ++ )
                f[i][j] = MAX( f[i][j], f[i][k - 1] + f[k + 1][j] + su[i][j][k] * su[i][j][k] );
    }
    write( f[1][M] ), putchar( '\n' );
    return 0;
}
posted @ 2020-10-04 09:55  crashed  阅读(130)  评论(0编辑  收藏  举报