Description
给你一个长度为 (n) 的环状序列,每一个位置有一定权值,让你选出恰好 (m) 个位置,使得选出的位置两两不相邻并且权值和最大。求最大权值和。
(1leq nleq 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 <bits/stdc++.h>
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
", ans);
return 0;
}