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\)

思路

双倍经验:P1182 数列分段 Section II

思路

这道题明显可以二分答案。

好的,接下来如何写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;
}
posted @ 2022-03-24 12:57  蒟蒻xiezheyuan  阅读(65)  评论(0编辑  收藏  举报