CodeForces-1490G-Old Floppy Drive

题目大意

img

给定一个长度为n的数组,有m组查询,每组查询包含一个正整数xi,初始指针指向a[1],每个一个单位时间指针向后移动,即a[i]移动一个位置到达a[i+1],a[n]移动一个位置后到达a[1],要使得所有数字之和>=xi, 问至少移动多少次?如果无法达到>=x,输出-1

思路

设sum[i] 表示a[1]+a[2]+…+a[i](即前缀和),maxx[i]表示max{sum[1],sum[2],…,sum[i]}显然maxx是非递减序列.

如果maxx[n]>=x, 说明答案一定在1 ~ n,根据maxx[i]非递减性质可以使用二分找到第一个下标i满足maxx[i]>=x.

如果maxx[n]<x:

​ 1)如果sum[n]<=0,并且maxx[n]<x,循环一圈之后答案总和反而减少,说明永远不可能到达x,故输出-1

​ 2)如果sum[n]>0:

​ sum[i+n]=sum[i]+sum[n];

​ 设d=x-maxx[n],要使得答案>=x,那么至少要循环ceil(d/sum[n])圈

​ 由于第一圈以内答案到达maxx[n]后都需要循环ceil(d/sum[n])圈,显然其余maxx[i]也至少需要循环 ceil(d/sum[n])圈,所以可以找到最小的maxx[i](maxx[i]=x-ceil(d/sum[n]) *sum[n])使得让它循环ceil(d/sum[n])圈后>=x,二分查找下标i即可。这样最后的答案就是ceil(d/sum[n]) *sum[n]+i.注意问的是移动多少次,所以答案要-1.

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+50;
ll n,m,T,a[maxn],sum[maxn],maxx[maxn],ans[maxn];
int main(){
	cin>>T;
	while(T--){
		cin>>n>>m;
		memset(maxx,0,sizeof(ll)*(n+10));
		for(int i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]+a[i],maxx[i]=max(maxx[i-1],sum[i]);
		for(int i=1;i<=m;i++){
			ll x;cin>>x;
			if(x<=maxx[n]){
				cout<<lower_bound(maxx+1,maxx+1+n,x)-maxx-1<<' ';
			}
			else{
				if(sum[n]<=0){
					cout<<-1<<' ';
				}
				else{
					ll d=x-maxx[n];
					ll tot=ceil(d*1.0/sum[n]);
					x-=tot*sum[n];
					ll t=lower_bound(maxx+1,maxx+1+n,x)-maxx;
					t=t+tot*n-1;
					cout<<t<<' ';
				}
			}
		}
		cout<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenix_ZengHao

创作不易,能否打赏一瓶饮料?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值