Codeforces 教育场 41 - B - 前缀和

前缀和,复杂度O(1), 高效降维

题意:

你和你的一个基友在听微积分讲座,你的基友迷迷糊糊的,时醒时睡,大概是昨晚干了什么不可告人的事,身为他的基友,你有一个技能,不过由于你很虚,只能释放一次该技能,该技能就是,你发动时,你的基友会保持苏醒状态b分钟,由于你的基友是个大佬,一听就会,他醒着的时候,只要听到目前讲座上讲的所有定理,就都能学会。

给两个数组n[], t[], n[]数组表示每个讲座所讲的定理个数,t[]数组表示你基友的状态,0就是睡了,1就是醒了。

再给俩数a, b。a是讲座的个数,b是你放的技能效果持续时间。问你最佳放技能时间,即何时放技能,你的基友能学会最多的定理。

样例:

6 3
1 3 5 2 5 4
1 1 0 1 0 0

输出:

16

第三个讲座时放技能即可,前两个讲座,你的基友醒着,一共掌握了1 + 3 = 4个定理,然后这货睡了,你就给个技能,持续三个讲座时间,即第三,四,五个讲座你基友醒了,又掌握了5 + 2 + 5 = 12个定理,一共最多掌握16个定理。


思路:

利用前缀和把n方的复杂度降到O(n), 题内复杂度是O(n), 前缀和复杂度仍为O(1)。

先看他醒着的时候,即t[i] == 1时,记一个cnt += n[i]; 求出你没放技能前你的基友就能掌握的定理数,然后把这些t[i] == 1对应的n[i]全赋值为0,这样用前缀和维护的时候就不会多加已经算过的定理数了,

sum[0] = 0;

for(int i = 1; i <= a; i++) sum[i] = sum[i - 1] + n[i];

以上两步就是求进行到 i 的前缀和,然后再记一个maxT在区间内维护即可,最后maxT + cnt即为所求。


代码如下:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <ctime>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL Inf = 1e18 + 7;
const int Maxx = 1e5 + 7;
int a, b;
int n[Maxx];
int t[Maxx];
int sum[Maxx];

int main() {
    scanf("%d %d", &a, &b);
    for(int i = 1; i <= a; i++) scanf("%d", &n[i]);
    for(int i = 1; i <= a; i++) scanf("%d", &t[i]);
    int cnt = 0;
    for(int i = 1; i <= a; i++) if(t[i] == 1) cnt += n[i];
    for(int i = 1; i <= a; i++) if(t[i] == 1) n[i] = 0;
    sum[0] = 0;
    for(int i = 1; i <= a; i++) sum[i] = sum[i - 1] + n[i];
    int tmp;
    int maxT = 0;
    for(int i = 1; i <= a - b + 1; i++) {
        tmp = sum[i + b - 1] - sum[i - 1];
        maxT = max(maxT, tmp);
    }
    printf("%d\n", maxT + cnt);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值