题解:CF2025D Attribute Checks

· · 题解

CF2025D Attribute Checks

题目大意

给定两个正整数 nm,以及一个长度为 n 的数组 r。保证 -m \le r_i \le m,并且恰好有 mr_i0

你有两个初始值均为 0 的变量 IS,接下来在第 i 秒中(1 \le i \le n)将发生如下事件:

求你最终能获得分数的最大值

数据范围
0\leq m \leq 5000 m < n \leq 2\cdot 10^6
时空限制

我们发现,只有当 r_i = 0 的时候才能使 S,I 的值改变,换言之,每次在 r_i 处进行决策。 考虑dp,我们可以建立数组 dp[i][j] 表示 I=i ,S=j 时候的获得分数的最大值,显然,这时候是第 i+j 次决策。

每一次决策我们可以从 dp[i-1][j]dp[i][j-1] 转移而来。

我们将 0 定义为“决策数” 每一次决策过后,会增加一些分数,如果 I 增加为 I+1,那么增加的分数将会是在这个“决策数”之后的 r_i=I+1 的数量,因为当上一次对 I 进行增加时,已经将 r_i=I 所获的分数加过了。

对于获得每一次决策后,这个“决策数”后面每一个数字的数量 m^2 打桶即可。

    #include<bits/stdc++.h>
using namespace std;
#define ll long long
int pot1[5010][5010],pot0[5010][5010];int n,m,cnt;
//pot1[i][j]代表第m-i次决策的r后面大于0的数字j 的个数
//pot1[i][j]代表第m-i次决策的r后面小于0的数字,绝对值j 的个数
ll dp[5010][5010];
int main(){
    //freopen("read.in","r",stdin);
    stack<ll>st;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        ll l;
        scanf("%lld",&l);
        st.push(l);
    }
    while(!st.empty()){
        int l=st.top();
        st.pop();

        if(l==0){
            cnt++;
            for(int i=0;i<=m;++i)pot1[cnt][i]=pot1[cnt-1][i],pot0[cnt][i]=pot0[cnt-1][i];
        }
        if(l<0)pot0[cnt][-l]++;
        if(l>0)pot1[cnt][l]++;
    }
    for(int i=1;i<=m;++i){
        for(int j=0;j<=m;++j){
            if(j-1>=0)dp[j][i-j]=max(dp[j][i-j],dp[j-1][i-j]+pot0[m-i][j]);
            if(i-j-1>=0)dp[j][i-j]=max(dp[j][i-j],dp[j][i-j-1]+pot1[m-i][i-j]);
        }
    }
    ll mxn=0;
    for(int i=0;i<=m;++i)mxn=max(mxn,dp[i][m-i]);
    printf("%lld",mxn);
}