前缀和,复杂度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;
}