1. 程式人生 > >SSL2834 2017年11月4日提高組T2 揹包(二分)

SSL2834 2017年11月4日提高組T2 揹包(二分)

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); }