二分答案(技巧)

牛牛爱学习.

题目描述
疫情期间,牛牛宅在家里无事可做,于是就在网上买了n本书,每本书都有一个知识值为ai。每读一本书,牛牛的知识力就会上升ai点。当然了,因为牛牛的精力也是有限的,如果同一天连续读k本书,获得的知识力只能增加ai-k+1点。比如第一天看了知识值为5的书,那么牛牛会获得5点知识力,如果这一天在继续看另一本知识值为5的书,只能获得4点知识力,如果看了前面两本书后在继续看一本知识值为2的书,就只能获得0点知识力。牛牛想知道如果他要获得m点知识力,最少需要看几天。

注意:看书不需要按顺序,一本书只能看一次,书可以不看完,只要看就会增加知识力,当书增加的知识力为负时候可以选择不看,可以认为看完一本书是一瞬间的事情,看完后书就会消失。
输入描述:
第一行输入一个n(1≤n≤106)和m(1≤m≤109),第二行输入每本书的知识值ai(0≤ai≤109)。
输出描述:
输出最少要多少天才能获得大于等于m点的知识力,如果无法获得,请输出-1。
示例1
输入
4 10
4 4 4 4
输出
1
说明
第一天看完四本书知识力增加 4+3+2+1=10
示例2
输入
3 20
8 6 6
输出
3

分析:直接求解有点困难,所以二分答案,判断该答案可不可行,如果可行就判断更少的天数行不行,反之,增加天数;
#include<bits/stdc++.h>
#define ll long long 
#define mod 1000000007
#define pi acos(-1)
using namespace std;
int n,m;
int a[1000005];
int b[1000005]={0};				//记录同一天看的书的数量
int check(int mid){
	int sum = 0;
	memset(b,0,sizeof(b));				
	for(int i = 0; i < n; i++){
		sum+=max(0,a[i]-b[i%mid]);	//知识力减去当天看的书的数量-1
		b[i%mid]++;				//第i%mid天看的数量+1
		if(sum>=m) return 1;
	}
	return 0;
}
int main(){
	cin>>n>>m;
	for(int i = 0; i < n; i++)
		cin>>a[i];
	int l = 1, r = n;
	int ans=-1;
	sort(a,a+n,greater<int>());			//降序,保证先看知识力多的
	while(l<=r){				//二分答案
		int mid = (l+r)>>1;
		if(check(mid)){
			ans=mid;
			r=mid-1;
		}else l=mid+1;
	}
	cout<<ans<<endl;

	return 0;
}

你可能感兴趣的:(题解)