【题目描述】
蛤布斯有n种商品,第i种物品的价格为ai,价值为bi。有m个人来向蛤布斯购买商品,每个人每种物品只能购买一个。第j个人有cj的钱,他会不停选择一个能买得起的价格最高的商品买走(如果有多个则选择价值最高的)。你需要求出每个人购买的物品的价值和。
【输入数据】
第一行两个正整数n,m。接下来n行每行两个正整数ai,bi。接下来m行每行一个正整数cj。
【输出数据】
m行,每行一个整数表示答案。
【样例输入】
5 4
10 5
9 8
7 3
3 4
1 2
20
100
28
18
【样例输出】
15
22
18
10
【数据范围】
对于20%的数据,n,m<=1000。
对于另外30%的数据,ai,bi,cj在[1,10^12]中均匀随机。
对于100%的数据,n,m<=100000,ai,bi,cj<=10^12。
【题解】
50分解法,将商品以价格为第一关键字、以价值为第二关键字排序,对于每个顾客从大到小枚举,并累计价值和,最后输出。
100分解法,因为商品时有序的,可以想到二分查找,log的复杂度可以明显降低100000的范围带来的影响,但仅仅每次而分出当前可以买到的最大价格的商品是不能得满分的;回归排序操作,可以想到用前缀和来求出一整个可以连续买下的区间的的价格和以及价值和。综上,现二分出起点,再二分出区间并累计,这就是满分算法。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=101111; typedef long long ll; int n,m; bool vis[N]; ll cnt[N]; struct gds{ll w,c;}g[N],ind[N]; bool cmp(gds x,gds y) { if(x.w>y.w)return 1; if(x.w<y.w)return 0; return x.c>y.c; } int b1(ll key) { int l=0,r=n-1,mid; while(l<r){ mid=(l+r)>>1; if(g[mid].w>key)l=mid+1; else r=mid-1; } while(g[l].w>key)l++; return l; } int b2(int j,ll key) { int l=0,r=n-1,mid; while(l<r){ mid=(l+r)>>1; if(ind[mid].w-ind[j].w+g[j].w>key) r=mid-1; else l=mid+1; } while(ind[r].w-ind[j].w+g[j].w>key)r--; return r; } int main() { freopen("pack.in","r",stdin); freopen("pack.out","w",stdout); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%I64d%I64d",&g[i].w,&g[i].c); sort(g,g+n,cmp); ind[0].w=g[0].w; ind[0].c=g[0].c; for(int i=1;i<n;i++){ ind[i].w=ind[i-1].w+g[i].w; ind[i].c=ind[i-1].c+g[i].c; } for(int i=0;i<m;i++){ ll c; int k0=-1,j0=n; scanf("%I64d",&c); memset(vis,0,sizeof(vis)); for(int j=b1(c);;j=b1(c)){ if(j0<=j&&j<=k0)break; if(j>n-1||c-g[j].w<0)break; int k=b2(j,c); c-=(ind[k].w-ind[j].w+g[j].w); cnt[i]+=(ind[k].c-ind[j].c+g[j].c); j0=j; k0=k; } } for(int i=0;i<m;i++) printf("%I64d ",cnt[i]); fclose(stdin); fclose(stdout); return 0; }