题目大意
有 N ( ≤ 2 × 1 0 5 ) N(\le 2\times 10^5) N(≤2×105) 盏灯,你可以把其中 R R R 盏染成红色,其他染成蓝色。当第 i i i 盏和第 i + 1 i+1 i+1 盏灯颜色不同时,你将获得 A i ( 1 ≤ A i ≤ 1 0 9 ) A_i(1\le A_i\le 10^9) Ai(1≤Ai≤109) 的收益,问最多可以获得多少收益。
思路
当
2
R
>
N
2R>N
2R>N 时,可以将
R
R
R 换成
N
−
R
N-R
N−R。
可以证明,最优方案染红的灯一定是不连续的。
因此转化为,你将
R
R
R 盏灯染红,将第
i
i
i 盏灯染红可以获得
A
i
−
1
+
A
i
A_{i-1}+A_i
Ai−1+Ai 的收益,要求红灯不连续,求最大收益。(约定
A
0
=
A
n
=
0
A_0=A_n=0
A0=An=0)
记
f
(
x
)
f(x)
f(x) 为
R
=
x
R=x
R=x 时的答案。
f
(
x
)
f(x)
f(x) 不方便直接求,但可以发现
f
(
x
)
f(x)
f(x) 是一个上凸函数
二分一下斜率
k
k
k,可以
O
(
n
)
O(n)
O(n) 求出
f
(
x
)
−
k
x
f(x)-kx
f(x)−kx 的极值点
当极值点为
R
R
R 时就可以算出答案
代码
注意数据范围
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
using namespace std;
typedef long long ll;
const int N = 200005;
int n, R;
int a[N];
int b[N];
class node {
public:
int num;
ll val;
node() {}
node(int _num, ll _val) : num(_num), val(_val) {}
bool operator<(const node &rhs) const {
return val == rhs.val ? num < rhs.num : val < rhs.val;
}
node operator+(const node &rhs) const {
return node(num + rhs.num, val + rhs.val);
}
};
node max(node x, node y) { return x < y ? y : x; }
node check(int k) {
node f0(0, 0), f1(0, 0);
rep(i, 1, n) {
node tmp = max(f0 + node(1, b[i] - k), f1);
f0 = max(f0, f1);
f1 = tmp;
}
// printf("%d: %d %d\n", k, f1.num, f1.val);
return f1;
}
void work() {
int l = 0, r = 2e9;
while (l < r) {
int mid = (1ll + l + r) / 2;
if (check(mid).num < R)
r = mid - 1;
else
l = mid;
}
ll ans = check(l).val + 1ll * r * R;
printf("%lld\n", ans);
}
int main() {
scanf("%d%d", &n, &R);
R = min(R, n - R);
rep(i, 1, n - 1) { scanf("%d", &a[i]); }
rep(i, 1, n) { b[i] = a[i - 1] + a[i]; }
work();
return 0;
}