codeforces E. Superhero Battle(超级英雄)

题目链接:http://codeforces.com/contest/1141/problem/E
题目大意:简单来说就是:一只怪兽和一位超级英雄正在作战,这只怪兽初始血量是H,然后给你一个数组d,每过一个单位。怪兽血量H变为H+di。战斗是无止尽的,也就是说,数组是一个“环形”的。然后问你,怪兽血量最早减为0或者更少(即H <= 0)的时刻,如果不能变成0则输出-1.
题解思路:先考虑输出-1的情况:第一轮H没有减为0,而且H的血量没有减少。也就是说,在第一轮的时候,H血量最低的时候 H >= 0,在最后 H1 >= H。因此可以预处理一下d数组,求前缀和,在求前缀和的过程中可以判断上述情况是否成立。
1)我们可以计算当前前缀和sum[i]+H是否小于0,如果小于0,则可以输出结果了。
如果sum[n] >= 0,则说明上述条件是成立的(因为由上面可以知道一圈之内H没有减为0(否则会直接输出结果)),则输出-1,退出。

现在就是计算可以减为0的情况了。
2)按照每轮血量减少sum[n],直到该轮血量<=0停止,但是这样会TLE,当sum[n]的绝对值非常小而H非常大时。所以这种暴力不行。我们此时注意到H的值是递减的,因此我们可以二分求解,求一个boss血量<=0 的最小轮数。这种思路是没问题的,但是答案是错的,因为很有可能boss已经提前凉了。而且很有可能在前几轮就已经GG了。因此我们需要注意这种情况。怎么避免呢?我们只需要记录每轮boss的血量的最低值,也就是每轮能够让boss血量损失最多的时刻。这样我们可以二分搜索boss血量最低的时候并且H<=0的最小轮数。因此在预处理的时候可以记录一下最小值。总结一下就是:预处理得到一个每轮boss损失的血量sum和每轮boss最多损失的血量mn。二分搜索H+mn<=0的最小轮数
最后,不要忘记遍历一次找到最先减为0的时刻。

时间复杂度的话大概也许可能是O(N);
代码:

#include <bits/stdc++.h>

using namespace std;

#define llong long long
#define inf 1000000000000
int main(){
	ios::sync_with_stdio(false);
	llong H, sum, ans, mn;
	int n, d[200005];
	cin>>H;cin>>n;
	sum = 0;mn = 0;ans = 0;
	for(int i = 0; i < n; i++){
		cin>>d[i];sum += d[i];
		mn = min(mn,sum);
		if(mn+H <= 0){cout<<i+1;exit(0);}
	}
	if(sum >= 0){cout<<-1;exit(0);}

	llong right, left, cnt;
	left = 0; right = H/(-sum)+1;
	while(left < right){
		llong h = H;
		llong mid = (left + right)/2;
		h += mid*sum;
		if(h + mn <= 0){
			cnt = mid;
			right = mid;
		}else {
			left = mid + 1;
		}
	}
	ans += cnt*(llong)n;
	H += sum*cnt;

	for(int i = 0; i < n&&H > 0; i++){
		H += d[i];
		ans++;
	}
	cout<<ans; 
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值