【bzoj4590】[Shoi2015]自动刷题机 二分

题目描述

一个数,初始为 0 。 l 次操作,每次给这个数加上 xi ( xi 可能为负),如果其小于 0 则变成 0 ,如果大于等于 n 则变成 0 并给 ans 加 1 。已知最后的 ans 等于 k ,求 n 可能的最小值和最大值。如果没有满足条件的 n ,输出 -1 。

输入

第一行两个整数 l,k,表示刷题机的日志一共有 l 行,一共了切了 k 题。
第二行 l 个整数,x1…xl。xi>=0表示写了 xi 行代码。xi<0 表示删除了这道题的 -xi 行代码。
1<=l,k<=100000,|xi|<=10^9

输出

输出两个数 a,b。分别代表 n 可能的最小值和最大值。如果不存在这样的 n 则输出 -1 。

样例输入

4 2
2
5
-3
9

样例输出

3 7


题解

二分

显然随着 ans(n) 的增加,答案单调不增长。。。于是二分。。。没了。。。

二分答案,直接模拟判断能否满足条件。

求最小值时二分最小的ans,使得结果小于等于k;求最大值时二分最大的ans,使得结果大于等于k。

注意二分出来结果以后需要代入检验一下结果是否等于k,因为求的是结果小于等于k的,可能取不到k。

时间复杂度$O(nlog a)$

#include <cstdio>
typedef long long ll;
ll a[100010];
int n;
int solve(ll mid)
{
	int i , ans = 0;
	ll now = 0;
	for(i = 1 ; i <= n ; i ++ )
	{
		now += a[i];
		if(now < 0) now = 0;
		if(now >= mid) now = 0 , ans ++ ;
	}
	return ans;
}
int main()
{
	int i , m;
	ll l , r , mid , ans;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a[i]);
	l = 1 , r = 1000000000000000ll , ans = -1;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(solve(mid) <= m) ans = mid , r = mid - 1;
		else l = mid + 1;
	}
	if(ans == -1 || solve(ans) != m) puts("-1");
	else
	{
		printf("%lld " , ans);
		l = 1 , r = 1000000000000000ll , ans = -1;
		while(l <= r)
		{
			mid = (l + r) >> 1;
			if(solve(mid) >= m) ans = mid , l = mid + 1;
			else r = mid - 1;
		}
		printf("%lld
" , ans);
	}
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/7762552.html