训练3.08(Gym101201J:Shopping)

题目:Gym101201J

Shopping
The sale bin of Big Box Bargains contains n products in a row. The ith item has price ai per unit.
There is no limit to the quantity of any item.
There are q customers who will enter the store to buy items. The ith customer has vi dollars, starts at item li and walks to the right to item ri (inclusive), one item at a time.
Each time they encounter an item, they will buy as many units of the item as they can afford.
You are now wondering, for each customer, how much money they will have left after buying items.

Input
The first line of input contains two space-separated integers n and q (1 ≤ n, q ≤ 200,000).
The next line of input contains n space-separated integers ai (1 ≤ ai ≤ 10^18).
Each of the next q lines contains three space-separated integers vi (1 ≤ vi ≤ 1018), li, and ri(1 ≤ li ≤ ri ≤ n).

Output
For each of the q customers, print, on a single line, a single integer indicating the remaining amount of money after shopping.

题意:有n个商品排成一行,每一个具有单价,且商品数量无上限。顾客i带着钱vi前来买买买,将从标号为li开始到、到ri结束的这一区间,一遇到可以购买的商品,就买,直到余额购买不了当前商品,再跳到下一个商品,继续买买买,直到区间结束。一共有q个顾客。问每个顾客在其对应区间完成购买后,还剩多少钱。

题解:
一个数最多被取模log次,既对m<=n,n%m<n/2(摘自大佬题解)

所以一个数最多就是被模log次,那么难道真的就让vi在li到ri区间中一个一个遍历找到他可以被取模的对象码吗,这样的话复杂度就是nq,会T。所以现在应该解决的是如何在某一个区间中找到当前应该作为模数的那个数(商品),这个数x的特征是:vi>=x且vi是当前区间[li,ri]第一个比vi小或者等于vi的数。想到这点后第一感觉就是用二分,但是,商品的价格却是无序的。

采用线段树,维护区间最小值。查找无序区间中第一个符合要求的数时,当当前左区间的最小数比目标小的话,前往左区间进行查找,反之就右区间。详细见代码。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=2e5+100;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll T[maxn*4];
ll a[maxn];
void build(ll l,ll r,ll id){
	if(l==r){
		T[id]=a[l];
		return;
	}
	ll mid=(l+r)>>1;
	build(l,mid,id*2);
	build(mid+1,r,id*2+1);
	T[id]=min(T[2*id],T[2*id+1]);
}
ll query(ll askl,ll askr,ll L,ll R,ll id,ll v){
	if(T[id]>v)return -1;
	if(L==R){
		if(a[L]<=v)
		return L;
		return -1;
	}
	ll mid=(L+R)>>1;
	ll tmp=inf;
	ll ans=-1;
	if(T[id*2]<=v&&askl<=mid)ans=query(askl,askr,L,mid,2*id,v);
	if(ans==-1&&T[id*2+1]<=v&&askr>mid)return query(askl,askr,mid+1,R,id*2+1,v);
	return ans;
}
int main(){
	ll n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	build(1,n,1);
	ll v,l,r;
	for(int i=1;i<=q;i++){
		scanf("%lld%lld%lld",&v,&l,&r);
		while(1){
			if(r<l)break;
			ll mni=query(l,r,1,n,1,v);
			if(mni==-1)break;
			if(v<a[mni])break;
			v=v%a[mni];
			if(v==0)break;
			l=mni+1;
		}
		printf("%lld\n",v);
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值