【ABC218H】Red and Blue Lamps(带权二分)

该博客讨论了一种优化问题,涉及在一定限制下最大化收益。问题设定有N盏灯,可以选择R盏染成红色,其余染成蓝色,不同颜色相邻的灯会带来收益。当2R>N时,可以通过转换策略改变R。博主证明了最优解中红灯不连续,并通过动态规划和二分查找的方法在O(n)时间内找到最大收益。代码实现中包括了数据范围的注意和主要函数的定义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目大意

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(1Ai109) 的收益,问最多可以获得多少收益。

思路

2 R > N 2R>N 2R>N 时,可以将 R R R 换成 N − R N-R NR
可以证明,最优方案染红的灯一定是不连续的。
因此转化为,你将 R R R 盏灯染红,将第 i i i 盏灯染红可以获得 A i − 1 + A i A_{i-1}+A_i Ai1+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值