P8198 背单词的小智
题面
小智在刚刚结束的 CET-4 考试中顺利通过了!现在,他要开始备战 CET-6 了。
可是,他的单词功底太差了,于是他准备开始摆烂背单词。
可是,人脑的能力是有限的,小智的大脑每一天都会有一个记忆的上限,如果超过这个上限,再多的单词也记不下了。
有一天,小智在背单词的时候想到了一个问题,你能帮帮他吗?
小智一共有 \(n\) 个单词需要背,第 \(i\) 个单词所拥有的「精神值」为 \(a_i\)。
小智每天的记忆力都是有上限 \(m\) 的。如果小智在第 \(i\) 天内背完了 \([l,r]\) 内的单词,那么这些单词将会占用小智 \(C_i = \sum\limits_{j=l} ^ r a_j^2\) 的记忆力。
小智需要在最多 \(k\) 天里把这些单词全部背完,他希望你在把这些单词背完的同时,让他每天需要的记忆力的最大值尽可能小。
也就是说,你需要将一个序列最多分为 \(k\) 段,请你找到一个最小的 \(m\),使得 \(\forall 1 \leq i \leq k\),\(C_i= \sum\limits_{j=l} ^ r a_j^2 \leq m\),其中 \(l\),\(r\) 为各段的左、右端点。
数据规模与约定
对于所有测试点,保证 \(1 \leq n \leq 1 \times 10^5\),\(1 \leq k \leq n\),\(1\leq a_i \leq 1\times 10^6\)。
思路
思路
这道题明显可以二分答案。
好的,接下来如何写check
函数呢?可以贪心,对于每一个数据,能加上就加上,不能加就重新开一个段。
然后就可以在check
函数中遍历数组,进行累加。如果累加中有一个地方大于了 \(k\),那么计数器++,并把累加器赋值为当前 \(a_i\) 的值。最后特判计数器 $ \lt k$.
代码
记得开long long
!
#include <algorithm>
#include <iostream>
#define int long long
#define SIZE (int)(1e5+5)
using namespace std;
int n,m,lft=0,rgt=0;
int arr[SIZE];
bool check(int x) {
int total=0,number=0;
for(int i=1; i<=n; i++) {
if(total+arr[i]<=x) {
total+=arr[i];
} else {
total=arr[i];
number++;
}
}
if(number>=m) {
return true;
} else {
return false;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>arr[i];
arr[i]=arr[i]*arr[i];
lft = max(lft,arr[i]);
rgt += arr[i];
}
while(lft<=rgt) {
int mid = (lft+rgt)>>1;
if(check(mid)) {
lft=mid+1;
} else {
rgt = mid-1;
}
}
cout<<lft;
return 0;
}