题意: 给你一个环形的磁盘,对于磁盘的n个区域,每个区域有一个整数
a
i
a_i
ai,你每次会将指针放到1号位置,它从1走到n,因为是环形,n之后又是1。
他对你进行m次询问,每次询问有一个数x,每当你的指针指向一个区域,会对贡献加上当前区域的值(可重复),当贡献至少为时,指针停下,让你输出指针每次走的步数。
思路: 显然,我们可以对走过的圈数进行二分,因为这显然有单调性。
证明:我们使用
s
u
m
i
sum_i
sumi记录前i项的和。
如果
s
u
m
n
>
0
sum_n>0
sumn>0,那么单调性显然成立,因为如果当前满足,显然可以通过试着减少圈数去满足。如果当前不满足,就必须通过增加圈数来满足。
如果
s
u
m
n
<
0
sum_n<0
sumn<0,那么单调性也成立,因为如果当前满足,减少圈数必满足。如果当前不满足,就要通过减少圈数满足。
在我们二分了圈数之后,就需要考虑剩下的部分怎么处理。
假设我们当前走了
a
a
a圈,那么现在就有
a
∗
s
u
m
n
a*sum_n
a∗sumn的贡献,还需要至少
x
−
a
∗
s
u
m
n
x-a*sum_n
x−a∗sumn的贡献,那么就是在前缀和数组中找到第一个
i
i
i,使得
s
u
m
i
≥
x
−
a
∗
s
u
m
n
sum_i\ge x-a*sum_n
sumi≥x−a∗sumn,这时候就用
s
t
st
st表维护一下前缀和数组的区间最值,然后二分找第一个合法的。
对
l
o
g
2
x
log_2x
log2x进行预处理,st表的查询复杂度降至
O
(
1
)
O(1)
O(1),代码总的复杂度为
O
(
m
l
o
g
n
l
o
g
n
)
O(mlognlogn)
O(mlognlogn)
代码:
#define int __int128
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
int x = 0,f = 1;
char ch = gc();
while(ch < '0' || ch > '9')
{
if(ch == '-')
f = -1;
ch = gc();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch-'0';
ch = gc();
}
return x * f;
}
int st[N][20],a[N],sum[N],lg[N];
int query(int k)
{
int t = lg[k];
return max(st[1][t],st[k-(1<<t)+1][t]);
}
void print(int x)
{
if (!x) return ;
if (x < 0) putchar('-'),x = -x;
print(x / 10);
putchar(x % 10 + '0');
}
signed main()
{
int t=read();
for(int i=2; i<N; i++)
lg[i] = lg[i>>1] + 1;
while(t--)
{
int n=read(),m=read();
for(int i=1; i<=n; i++) a[i]=read(),sum[i]=sum[i-1]+a[i],st[i][0]=sum[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=1; i+(1<<j-1)<=n; i++)
st[i][j] = max(st[i][j-1],st[i+(1<<j-1)][j-1]);
while(m--)
{
int x=read();
int l = 0,r = 1e9,ans = 1e18;
while(l<=r)
{
int mid = l+r>>1;
int y = x - mid * sum[n];
int ll = 1,rr = n,fg = 0;
while(ll<=rr)
{
int mm = ll + rr >> 1;
if(query(mm) >= y)
{
rr = mm - 1;
ans = min(ans,mid*n+mm-1);
fg = 1;
}
else
ll = mm + 1;
}
if(fg)
r = mid - 1;
else
{
if(sum[n]<=0)
r = mid - 1;
else l = mid + 1;
}
}
if(ans == 1e18)
print(-1);
else
{
if(!ans) printf("0");
else print(ans);
}
printf(" ");
}
puts("");
}
return 0;
}