[BZOJ 2151]种树

Description

题库链接

给你一个长度为 \(n\) 的环状序列,每一个位置有一定权值,让你选出恰好 \(m\) 个位置,使得选出的位置两两不相邻并且权值和最大。求最大权值和。

\(1\leq n\leq 200000\)

Solution

和[APIO/CTSC 2007]数据备份有点类似。

\(g(x)\) 为恰好选 \(x\) 个位置时,最大的权值和。可以发现 \(g(x)\) 是上凸的。因此考虑带权二分。二分上下界取位置上的权值最值。

\(f_{i,1/0,1/0}\) 为第一个位置选或不选,第 \(i\) 个位置选或不选获得的最大权值和。DP 只需考虑 \(i\)\(i-1\) 这两个位置选取与否。

对于 \(g\) 函数可能存在相邻若干点斜率相同的情况,还是和上一题一样的做法,转移时记录最少选点数。之后每次二分到选点数 \(\leq m\) 的值时更新答案。

Code

#include 
using namespace std;
const int N = 200000+5, inf = 1e9;

int n, m, f[N][2][2], a[N], g[N][2][2], tmp;

bool check(int mid) {
    f[1][1][1] = a[1]-mid, g[1][1][1] = 1;
    f[1][0][0] = 0, g[1][0][0] = 0;
    f[1][1][0] = f[1][0][1] = -inf;
    for (int i = 2; i <= n; i++)
        for (int j = 0; j <= 1; j++) {
            f[i][j][1] = f[i-1][j][0]+a[i]-mid;
            g[i][j][1] = g[i-1][j][0]+1;
            if (f[i-1][j][0] > f[i-1][j][1] || (f[i-1][j][0] == f[i-1][j][1] && g[i-1][j][0] < g[i-1][j][1]))
                f[i][j][0] = f[i-1][j][0], g[i][j][0] = g[i-1][j][0];
            else f[i][j][0] = f[i-1][j][1], g[i][j][0] = g[i-1][j][1];
        }
    int mf = -inf, mg = 0;
    for (int i = 0; i <= 1; i++)
        for (int j = 0; j <= 1; j++)
            if (!(i&j))
                if (f[n][i][j] > mf || (f[n][i][j] == mf && g[n][i][j] < mg))
                    mf = f[n][i][j], mg = g[n][i][j];
    tmp = mf+m*mid;
    return mg <= m;
}
int main() {
    scanf("%d%d", &n, &m);
    if (n/2 < m) {puts("Error!"); return 0; }
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    int l = -1000, r = 1000, ans = -1, mid;
    while (l <= r) {
        mid = (l+r)>>1;
        if (check(mid)) r = mid-1, ans = tmp;
        else l = mid+1;
    }
    printf("%d\n", ans);
    return 0;
}

你可能感兴趣的:([BZOJ 2151]种树)