CF1582 E. Pchelyonok and Segments

Problem - 1582E - Codeforces

 

把数组翻转一下

问题变成了每段数字个数增多,总和减少

这样的好处是可以直接求段数的最大值

dp[i][j]表示前i个数可以组合出前j段,且第j段的和最大时的最后一个位置

因为每一段的和逐渐减小,所以前面的和越大越好

j最大是根号级别,所以复杂度是n*sqrt(n)的

转移首先令f[i][j]=f[i-1][j],因为前i-1个数可以的前i个数也可以

然后考虑是否要用i更新f[i][j]

i可以更新f[i][j]有2个条件:

1、以i为结尾的长为j的一段能够接到前i-j个数凑成的j-1个段的后面,即前者的和 小于 后者的第j-1段的和的最大值

2、以i为结尾的长为j的一段的和 大于 原先的以f[i][j]结尾的长为j的一段的和

前缀和可以判断

 

#include<bits/stdc++.h>

using namespace std;

#define N 100003

int a[N];
int f[N][447];

long long sum[N]; 

int main()
{
    int T,n;
    int m,l,mid,r;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        reverse(a+1,a+n+1);
        for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
        l=1;
        r=n;
        while(l<=r)
        {
            mid=l+r>>1;
            if(1ll*mid*(mid+1)/2<=n)
            {
                m=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                f[i][j]=0;
        for(int i=1;i<=n;++i)
        {
            f[i][1]=f[i-1][1];
            if(a[i]>a[f[i][1]]) f[i][1]=i;
        }
        for(int i=1;i<=n;++i)
            for(int j=2;j<=m && i-j>=0 && f[i-j][j-1];++j)
            {
                f[i][j]=f[i-1][j];
                if(sum[i]-sum[i-j]<sum[f[i-j][j-1]]-sum[f[i-j][j-1]-j+1])
                {
                    if(f[i][j])
                    {
                        if(sum[i]-sum[i-j]>sum[f[i][j]]-sum[f[i][j]-j]) f[i][j]=i;
                    }
                    else f[i][j]=i;
                }
            }
        for(int i=m;i;--i)
            if(f[n][i])
            {
                printf("%d\n",i);
                break;
            }
    }
}

 

posted @ 2021-11-07 18:06  TRTTG  阅读(94)  评论(0编辑  收藏  举报