Codeforces Round #702 (Div. 3) G. Old Floppy Drive
题意
给定一个包含(n)个整数的数组({a}),可以循环延申至无穷个元素(定义编号(n)的后一个元素为编号(1))
再给定(m)个询问(x),对于每个(x)
问无穷数组({a})的前缀和数组({S})中,第一次出现(S_ige x)的下标(i)是多少(输出时下标要(-1))
若不存在,输出(-1)
限制
(1le Tle 10^4)
(1le n,mle 2cdot 10^5)
(-10^9le a_ile 10^9)
(1le x_ile 10^9)
(sum nle 2cdot 10^5, sum mle 2cdot 10^5)
思路
注意求的是第一次出现(a_ige x)的下标(i),不是(a_i=x)(读错题了,可惜)
记(S_k=sum_{i=1}^k a_i, M_k=max_{i=1}^k S_i)
根据({M})的定义
如果(M_nge x),说明答案存在于(1)到(n)之间
又因为({M})是非递减数组,所以可以通过二分查找来直接找到最小下标
否则,对(S_n)的正负性质进行讨论
- 如果(S_nle 0),又因为此时(M_nlt x),所以不存在任何一种状态满足(S_ige x),故输出(-1)
- 如果(S_ngt 0),
根据题意,(S_{i+n}=S_i+S_n)
定义(d=x-M_n),表示询问的值与(1)到(n)中所能得到最大的值的差值
于是发现,要想得到大于等于(x)的数,(1)到(n)中的最大值(M_n)还需要加上(lceilfrac d {S_n} ceil)遍(S_n)才行
据上述,得到(M_n+lceilfrac d {S_n} ceil*S_nge x)
即(M_nge x-lceilfrac d {S_n} ceil*S_n)
故让(x)减去(lceilfrac d {S_n} ceil*S_n)后,便能通过二分查找来直接找到最小下标,最后将下标加上(lceilfrac d {S_n} ceil*n)即为答案
代码
(202ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mx[200050];
void solve()
{
int n,m;
cin>>n>>m;
ll s=0;
for(int i=1;i<=n;i++)
{
ll d;
cin>>d;
s+=d;
mx[i]=max(mx[i-1],s);
}
while(m--)
{
ll q;
cin>>q;
if(mx[n]>=q)
cout<<(lower_bound(mx+1,mx+1+n,q)-mx)-1<<' ';
else
{
if(s<=0)
cout<<"-1 ";
else
{
ll d=q-mx[n];
ll tim=(d+s-1)/s;
q-=tim*s;
cout<<(lower_bound(mx+1,mx+1+n,q)-mx+tim*n)-1<<' ';
}
}
}
cout<<'
';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;while(T--)
solve();
return 0;
}