Codeforces 933E:A Preponderant Reunion – 题解

主要思路

想了半天都不知道怎么刻画状态,后面发现可以转成一个泛化问题,就比较好理解了。

考虑不严格地减去最小值、而是任意值,并达到题目要求的结果。这个问题肯定不会更优,而最优解可以直接作为本题最优解。

如果是这样的话,那么我们可以试着先做一个区间 DP。设置 \(dp[l][r]\) 使得这一段区间全部清空、端点进行保留,那么构造出 \(c_l = p_l, c_i = \max(p_i – c_{i – 1}, 0)\),得到:

\[ \begin{aligned} f[l][r] &= \sum_{i = l}^{r – 2} c_i + c_{r – 1} + c_r \\ &= f[l][r – 2] + \max(p_r – c_{r – 1}, 0) + c_{r – 1} \\ &= f[l][r – 2] + \max(p_r, c_{r – 1}) \\ &\leq f[l][r – 2] + p_r \\ &= f[l][r – 2] + f[r][r] \end{aligned} \]

那么可以用这个直接做线性的 DP,讨论一下不同的长度,然后再反过去即可。

代码

// CF933E.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 3e5 + 200;

typedef long long ll;

int n, ai[MAX_N], pi[MAX_N];
ll f[MAX_N];
bool opt[MAX_N];
vector<int> ans;

int process(int pos)
{
    int val = min(ai[pos], ai[pos + 1]);
    if (val)
        ans.push_back(pos);
    ai[pos] -= val, ai[pos + 1] -= val;
    return val;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &ai[i]);
        ll single = f[max(0, i - 2)] + ai[i], binary = f[max(0, i - 3)] + max(ai[i], ai[i - 1]);
        f[i] = min(single, binary), opt[i] = f[i] != single;
    }
    int cnt = 0;
    for (int i = min(f[n - 1], f[n]) == f[n - 1] ? n - 1 : n; i >= 1; i -= (opt[i] == true) + 2)
        pi[++cnt] = i;
    reverse(pi + 1, pi + 1 + cnt);
    for (int i = 1; i <= cnt; i++)
    {
        int pre = pi[i - 1], curt = pi[i];
        ll acc = 0;
        if (opt[curt])
            acc += process(curt - 1);
        acc += process(pre + 1), acc += process(curt);
    }
    printf("%lld\n", 1LL * ans.size());
    for (auto &x : ans)
        printf("%d\n", x);
    puts("");
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *