2020牛客多校第八场K题 Kabaleo Lite(前缀和+小贪心)

本文介绍了Kabaleo Lite问题,强调了处理大整数范围的数据,如使用int128类型。通过预处理利润的前缀和与每道菜的使用次数,利用贪心策略选择最优解。当前缀和大于等于a[1]时,将其保存并按前缀和降序排列,最终实现问题的解决方案。
摘要由CSDN通过智能技术生成
原题:Kabaleo Lite
题面:

在这里插入图片描述

思路:

本题需要注意的是数据范围,可能会达到 ± 1 e 19 \pm1e^{19} ±1e19。用ULL可以处理正数,LL处理负数?不知道,我不会,我太菜了。普遍用 i n t 128 int128 int128 处理数据。
我们可以先预处理出 p r o f i t profit profit 的前缀和,以及每一道菜会被用的次数。第 i i i 道菜的选择次数是由它的前继菜品的最小数量和它的数量构成的,也就是: c n t [   i   ] = m i n ( c n t [   i − 1   ] , b [   i   ] ) cnt[\ i\ ]=min(cnt[\ i -1\ ], b[\ i\ ]) cnt[ i ]=min(cnt[ i1 ],b[ i ])
我们还可以筛选一下可用的前缀和。当某一个前缀和小于 a [   i   ] a[\ i\ ] a[ i ] 的时候,这个选择方案一定不是最优解(明明能选一个 1 1 1 能解决的事儿,为什么要选那么多个呢?),所以我们可以把 s u m [   i   ] ≥ a [   1   ] sum[\ i\ ] \ge a[\ 1\ ] sum[ i ]a[ 1 ] 的前缀和存到一个数组进去。
最后需要一点点贪心的思想,将可选用的数组以前缀和从大到小排序。能选就选。最后就是我们要的答案啦。
这里我顺便存了一下每一个前缀和到达的右端点,是为了更新选择的次数啦。

代码如下:
#include <bits/stdc++.h>
#define sc scanf
#define pf printf
using namespace std;
typedef long long LL;
typedef pair<__int128, int> PII;
const int N = 1e5 + 10;
int t, n, cnt[N], pre_cnt, b[N], a[N];
__int128 sum[N], max_profit;
vector<PII> v;

bool cmp_v(PII a, PII b)
{
    if(a.first == b.first)    return a.second > b.second;
    return a.first > b.first;
}

//__int128的输出。
inline void print(__int128 x)
{
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}

void solve(int cas)
{
    sc("%d", &n);
    v.clear();
    memset(cnt, 0x3f, sizeof cnt);
    for(int i = 1; i <= n; i++) sc("%d", &a[i]);
    for(int i = 1; i <= n; i++) sc("%d", &b[i]);
    
    //预处理前缀和,并将每个坐标能被用的次数统计一下。
    //因为菜品必须从1开始选择
    //所以每道菜能被选择的次数是它的前继中最少的菜品数
    for(int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1]+a[i];
        cnt[i] = min(cnt[i - 1], b[i]);
    }
    
    //筛选一下符合优解可选用的前缀和
    for(int i = 1; i <= n; i++)
        if(sum[i] >= a[1])
            v.push_back({sum[i], i});
    //小贪心思想,让前缀和大的在前面
    sort(v.begin(), v.end(), cmp_v);
    
    max_profit = 0, pre_cnt = 0;
    LL max_number = 0, pre_pos = n;
    for(int i = 0; i < v.size(); i++) {
        //如果当前前缀和可以被选择,则将它选完,并更新选择的次数和索引。
        if(v[i].second <= pre_pos && cnt[v[i].second] > pre_cnt) {
            max_profit += v[i].first * (cnt[v[i].second] - pre_cnt);
            max_number += (cnt[v[i].second] - pre_cnt);
            pre_cnt = cnt[v[i].second];
            pre_pos = v[i].second;
        }
    }
    //其实最大客流量就是b[1]。
    pf("Case #%d: %lld ", cas, max_number);
    print(max_profit);
    puts("");
}

int main()
{
    sc("%d", &t); for(int i = 1; i <= t; i++) solve(i);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值