SSL2834 2017年11月4日提高組T2 揹包(二分)
阿新 • • 發佈:2019-01-28
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);
}