2017年11月4日提高组T2 背包
Description
蛤布斯有n种商品,第i种物品的价格为ai,价值为bi。有m个人来向蛤布斯购买商品,每个人每种物品只能购买一个。第j个人有cj的钱,他会不停选择一个能买得起的价格最高的商品买走(如果有多个则选择价值最高的)。你需要求出每个人购买的物品的价值和。
Input
第一行两个正整数n,m。接下来n行每行两个正整数ai,bi。接下来m行每行一个正整数cj。
Output
m行,每行一个整数表示答案。
Sample Input
5 4
10 5
9 8
7 3
3 4
1 2
20
100
28
18
Sample Output
15
22
18
10
Hint
【数据规模和约定】
20%的数据,n,m<=1000。
100%的数据,n,m<=100000,ai,bi,cj<=10^12。
分析:每次二分一下当前能买的价值最高的物品,然后再二分一下能买的连续一段的长度。由于每次买走一段以后钱至少减少一半,因此每个人只会二分 log 次。
代码
#include <cstdio>
#include <algorithm>
#define N 100005
#define ll long long
using namespace std;
struct arr
{
ll x,y;
}a[N];
ll p[N],sumx[N],sumy[N],n,m;
int so(arr u,arr v)
{
if (u.x==v.x) return u.y>v.y;
return u.x>v.x;
}
int find(int o,ll sump)
{
int l=o,r=n;
while (l<r)
{
int mid=(l+r)/2;
ll sum=sumx[mid]-sumx[o-1];
if (sum<=sump) l=mid+1;
else r=mid;
}
if (sumx[l]-sumx[o-1]>sump) l--;
return l;
}
int main()
{
//freopen("pack.in","r",stdin);
//freopen("pack.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld%lld",&a[i].x,&a[i].y);
for (int i=1;i<=m;i++)
scanf("%lld",&p[i]);
sort(a+1,a+n+1,so);
for (int i=1;i<=n;i++)
{
sumx[i]=sumx[i-1]+a[i].x;
sumy[i]=sumy[i-1]+a[i].y;
}
for (int i=1;i<=m;i++)
{
ll ans=0;
int s=0;
while (p[i]>=a[n].x)
{
int l=s+1,r=n+1;
while (l<r)
{
int mid=(l+r)/2;
if (a[mid].x>p[i]) l=mid+1;
else r=mid;
}
s=find(l,p[i]);
p[i]-=sumx[s]-sumx[l-1];
ans+=sumy[s]-sumy[l-1];
if (s==n) break;
}
printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
}