文章中若有不严谨或错误的地方,欢迎在评论中指出QAQ
Description
给出长度为 \(n\) 的数组 \(a\),第 \(0\) 秒从 \(a_1\)开始,此时总和为 \(a_i\),每过一秒就总和加上下一个 \(a_i\),若加到 \(a_n\),那么下一秒将会从 \(a_1\) 开始加,给出 \(m\) 个询问,每个询问包含一个
\(x_i\),问第一次总和大于等于 \(x_i\) 时是在第几秒。
\(1 \le n, m \le 2 \cdot 10^5\) , \(-10^9 \le a_i \le 10^9\) , \(1 \le x \le 10^9\)
Solution
由题目,很容易想到用前缀和来做,然后将 \(x_i\) 处理一下,再二分到第一个大于等于 \(x_i\) 的位置,所以关键就是如何将前缀和变成有序的。
考虑题目问的是第一次大于等于的位置,所以若当前 \(a_i\) 是负的,那么若当前位置满足条件,其前面也一定满足条件,并且位置靠前。
所以当 \(a_i\) 为负数时,可以不用考虑其前缀和,也就是说若当前前缀和小于前一个前缀和,取前一个前缀和即可。
然后前缀和数组就变成严格递增的序列了,之后用二分找到满足条件的位置就行。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int a[N];
ll sum[N];
ll mx[N];
int main()
{
int t;
cin >> t;
while(t -- )
{
int n,m;
cin >> n >> m;
for (int i = 1 ; i <= n ; i ++ ) cin >> a[i];
for (int i = 1 ; i <= n ; i ++ )
{
sum[i] = a[i] + sum[i-1];
mx[i] = max(mx[i-1],sum[i]);
}
while(m -- )
{
ll x;
cin >> x;
auto it = lower_bound(mx+1,mx+1+n,x) - mx;
if(it <= n){
cout << it-1 << ' ';
continue;
}
if(sum[n] <= 0){
cout << -1 << ' ';
continue;
}
ll ans = (x - mx[n] - 1) / sum[n] + 1;
x -= ans * sum[n];
it = lower_bound(mx+1,mx+1+n,x) - mx;
cout << it + ans * n - 1 << ' ';
}
cout << endl;
}
return 0;
}