Educational Codeforces Round 170 (Rated for Div. 2) D题题解记录

大致题意:
a,ba,ba,b两种属性,给定一个数组ccc,长度为nnn,其中有mmm个为0001≤m≤5000;m<n≤2⋅1061≤m≤5000 ; m<n≤2⋅10^61m5000;m<n2106。如果ci=0c_i=0ci=0,那么可以选择一种属性,使其值加1,如果ci>0c_i>0ci>0,那么会有一次检查,如果a>cia>c_ia>ci,那么通过检查。ci<0c_i<0ci<0的时候检查bbb属性,如果b>∣ci∣b>\left | c_i \right |b>ci,那么通过检查。
现在询问,在某种分配策略情况下,最多可以通过多少检查。

sss为截至目前为止遇到的总的分配点数。我们首先可以想到一个很朴素的dpdpdp:设f[i][j]f[i][j]f[i][j]表示前iii个记录中有jjj个属性点分配给了aaa的前提下,通过的最多的检查数。那么可以得到转移方程:
ci=0:f[i][j]=max(f[i−1][j],f[i−1][j−1]) c_i=0:f[i][j]=max(f[i-1][j],f[i-1][j-1]) ci=0:f[i][j]=max(f[i1][j],f[i1][j1])
ci>0:f[i][j]=f[i−1][j]+1(j>=ci),f[i][j]=f[i−1][j](j<ci) c_i>0:f[i][j]=f[i-1][j]+1(j>=c_i),f[i][j]=f[i-1][j](j<c_i) ci>0:f[i][j]=f[i1][j]+1(j>=ci),f[i][j]=f[i1][j](j<ci)
ci<0:f[i][j]=f[i−1][j]+1(s+ci>=j),f[i][j]=f[i−1][j](s+ci<j) c_i<0:f[i][j]=f[i-1][j]+1(s+c_i>=j),f[i][j]=f[i-1][j](s+c_i<j) ci<0:f[i][j]=f[i1][j]+1(s+ci>=j),f[i][j]=f[i1][j](s+ci<j)
但是这样的时间复杂度比较高。
考虑优化:我们发现,多数操作花在了第二种和第三种情况中的加法。而第一种情况只有mmm次,非常少。那么我们可以考虑用差分数组来代替原先的加法,而对于第一种情况,把差分数组的信息暴力传给fff数组更新以后再转移方程。
我们维护一个f[i]f[i]f[i]的差分数组ddd,用来实现O(1)O(1)O(1)的区间加法操作。
每次遇到ci=0c_i=0ci=0的时候,我们把ddd的信息传给当前状态下的f[i]f[i]f[i],然后再更新f[i]f[i]f[i]

code

void solve() {
    int n, m;
    cin >> n >> m;
    int s = 0;
    vector<int> dp(m + 1);
    vector<int> cf(m + 2);
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        if (x == 0) {
            //还原
            int temp = 0;
            for (int j = 0; j <= m; j++) {
                temp += cf[j];
                cf[j] = 0;
                if (j <= s)
                    dp[j] += temp;
            }
            for (int j = m; j >= 1; j--) {
                dp[j] = max(dp[j], dp[j - 1]);
            }
            s++;
        } else if (x > 0) {
            cf[x]++;
            cf[m + 1]--;

        } else {
            if (s + x < 0) continue;
            cf[0]++;
            cf[s + x + 1]--;
        }
    }
    int temp = 0;//注意这里,如果c数组最后全是检查,那么我们就没机会把差分数组信息传回
    //来了 所以必须在结束以后再更新一次
    for (int j = 0; j <= m; j++) {
        temp += cf[j];
        if (j <= s)
            dp[j] += temp;
    }

    int mi = 0;
    for (int i = 0; i <= m; i++)
        mi = max(mi, dp[i]);
    cout << mi << endl;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值