题目链接: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;
}