codeforces 1197D-Yet Another Subarray Problem

传送门:QAQQAQ

题意:给你一个序列,求一个子序列a[l]~a[r]使得该子序列的sum(l,r)-k*(r-l+1+m+1)/m值是在所有子序列中最大的,并输出最大值

思路:比赛的时候使用O(n)写的,但是被hack了,因为O(n)无法记录当前距离下一次-k还有多少,若用单调队列维护也不知道前面应该弹出多少(可能现在把前面弹出是最优的,但是到后面可能因为个数还没到m的倍数,把前面加进去又是最优的),所以我们考虑再加一维

法一:dp[i][j]表示序列到i截止,这一轮已经进行了j次取数(j=(len+m-1)%m),那么dp[i][j]维护的就是起点为s=i-j+1-m*t(t>=0)这个集合的最优,这样所有的dp[i][j]就可以维护以i截止的最优答案了

对于当前i更新有两种情况:第一种是只取当前这个a[i]这个在dp[i][1]特判即可

            第二种是还要取前面的,dp[i][j]从dp[i-1][j-1](因为dp[i-1][j-1]所维护的s集合和dp[i][j]所维护的s集合是一样的)转移即可(注意边界条件)

(之前HCY玄学又加了一层循环更新,一直看不懂,后来发现他虽然答案是对的,但m次更新中只有1次是有效的)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll inf=200000000000000;
 
int n,m;
ll ans=0,dp[300005][20],sum[300005],a[300005],k;
 
int main()
{
    scanf("%d%d%lld",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++) dp[i][j]=-inf;
    }
    dp[1][1]=a[1]-k; 
    for(int i=2;i<=n;i++)
    {
        dp[i][1]=a[i]-k;
        for(int j=1;j<=min(i,m);j++)
        {
            if(j==1) dp[i][j]=max(dp[i][j],dp[i-1][m]+a[i]-k);
            else dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i]); 
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++) ans=max(ans,dp[i][j]);
    }
    cout<<ans<<endl;
    return 0;
}//

法二:

和比赛时我的思路很像,只不过这里多加了一层维护start_point%m=rnd,进行m次尺取法即可(在时间够的情况下,搞不清楚当前单调队列弹出几个是最优的,那么就枚举,这样就不用担心前面要弹出什么了,只需在len%m=0时判断是否要把起始点重置即可)

即如果当前的和减去k*t小于0,那么就重新开始,否则继续加

注意ans在每一次向后扩展时都要更新

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300005;
ll ans=0,n,a[N],m,k;
 
int main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int rnd=1;rnd<=m;rnd++)
    {
        ll len=0; ll now=0;
        for(int i=rnd;i<=n;i++)
        {
            if(len%m==0) if(now-len/m*k<0) now=0,len=0;
            now+=a[i]; len++;
            ans=max(ans,now-(len+m-1)/m*k);
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Forever-666/p/11241525.html