【题目】
题目描述:
有 n 种物品,第 i 种物品的价格为 vi,每天最多购买 xi 个。
有 m 天,第 i 天你有 wi 的钱,你会不停购买能买得起的最贵的物品。你需要求出你每天会购买多少个物品。
输入格式:
第一行两个整数 n , m。接下来 n 行每行两个整数 vi , xi。接下来 m 行每行一个整数 wi。
输出格式:
共 m 行,每行一个整数,第 i 行表示第 i 天购买的物品数量。
样例数据:
输入
3 3 1 1 2 2 3 3 5 10 15
输出
2 4 6
备注:
【数据规模】
对于 20% 的数据,n , m ≤ 1000。
对于另外 40% 的数据,xi = 1。
对于 100% 的数据,n , m ≤ 100000,1 ≤ vi ≤ ,1 ≤ xi ≤ 10000,0 ≤ wi ≤ 。
【分析】
20%:直接暴力模拟购物的过程,时间复杂度O(n * m)
另外 40%:先对 n 件物品按照价格排序(下面以从大到小为例),处理出前缀和。每次询问的时候,我们二分找出买得到的最贵的物品,再二分出能买得起的一段区间 i ~ j 。由于买下 i ~ j 的所有物品后就买不起第(j + 1)件,并且第(j + 1)件物品的价格小于等于第 j 件物品的价格,因此至少会花费一半的钱,所以时间复杂度O()
100%:这个做法其实和40分做法类似,只改了一点,即每次二分能全部买下来的物品,再看下一个能买多少
【代码】
两次二分:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
struct node
{
int v,x;
}p[N];
long long sum[N],num[N];
bool comp(const node &a,const node &b)
{
return a.v>b.v;
}
int main()
{
// freopen("shop.in","r",stdin);
// freopen("shop.out","w",stdout);
int n,m,i,l,r,mid,now;
long long w,ans;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%d%d",&p[i].v,&p[i].x);
sort(p+1,p+n+1,comp);
for(i=1;i<=n;++i)
{
num[i]=num[i-1]+p[i].x;
sum[i]=sum[i-1]+1ll*p[i].v*p[i].x;
}
for(i=1;i<=m;++i)
{
ans=now=0;
scanf("%lld",&w);
while(w>=p[n].v&&now<n)
{
l=1,r=n;
while(l<r)
{
mid=(l+r)>>1;
if(p[mid].v<=w) r=mid;
else l=mid+1;
}
now=l,l--,r=n;
while(l<r)
{
mid=(l+r+1)>>1;
if(sum[mid]-sum[now-1]<=w) l=mid;
else r=mid-1;
}
w-=sum[l]-sum[now-1];
ans+=num[l]-num[now-1];
if(l==n) break;
if(w>=p[l+1].v)
{
ans+=w/p[l+1].v;
w%=p[l+1].v;
now=l+1;
}
now=l;
}
printf("%lld\n",ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
这是别的大佬用 lower_bound 实现的代码%%%:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
struct node
{
long long v,x;
}p[N];
long long sum[N],a[N],b[N],num[N];
bool comp(const node &a,const node &b)
{
return a.v<b.v;
}
int main()
{
// freopen("shop.in","r",stdin);
// freopen("shop.out","w",stdout);
int n,m,i,now,pos,temp;
long long w,ans;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%lld%lld",&p[i].v,&p[i].x);
sort(p+1,p+n+1,comp);
int tot=0;
for(i=1;i<=n;++i)
{
if(p[i].v!=p[i-1].v) p[++tot]=p[i];
else p[tot].x+=p[i].x;
}
n=tot;
sum[n+1]=num[n+1]=0;
for(i=n;i;--i)
{
sum[i]=sum[i+1]+p[i].x*p[i].v;
a[n-i+1]=sum[i];
b[i]=p[i].v;
num[i]=num[i+1]+p[i].x;
}
for(i=1;i<=m;++i)
{
ans=0;
scanf("%lld",&w);
now=n-(lower_bound(a+1,a+n+1,w)-a)+2;
w-=sum[now],ans+=num[now],--now;
while(now>=1&&w>=p[1].v)
{
pos=now;
now=lower_bound(b+1,b+now+1,w)-b;
if((p[now].v>w)||now>pos) --now;
if(now<1) break;
temp=min(p[now].x,w/p[now].v);
w-=p[now].v*temp,ans+=temp,--now;
}
printf("%lld\n",ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}