• 题意:有n杯果汁,m个小朋友。每杯果汁有三个量:美味度,每升价格,体积上限。然后可以在体积上限内自由搭混合果汁,美味度为所选的美味度的最小值。每个小朋友想要用不超过p块钱,买至少nL果汁,问每个小朋友能买到混合果汁的美味度最大值是多少?
  • 思路:求美味度最小值最大,显然可二分。多个二分的询问想到整体二分。
    果汁先按美味度排序,然后二分中[L,R]表示美味度范围,[l,r]表示小朋友集合。此时需要求得美味度在[mid+1,R]内的果汁满足小朋友要求的小朋友集合,转化为求小朋友p元最多能买多少L果汁。
    显然贪心,价格从小到大选择。然后就会想到果汁按p排序,预处理前缀和,然后二分。
    不过上面说的有问题:因为[R...]范围内的果汁也可能会影响我们的选择结果。
    我们改用易于修改的树状数组维护p排序。即下标为果汁价格p,维护V前缀和。为了方便二分还需要维护总价格的前缀和。
    整体二分中优先[mid+1,R],到(L==R)时,加入果汁的贡献[Update(g[L].p,g[L].v)]
    这样[L,R]时,[R..]后面这些都已经加入树状数组里面了。直接在树状数组上面二分,就可以算出p元能买的最大V。

讲得好啰嗦但是还是讲不清楚,看代码就能理解了~

  • code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
struct node {int w;ll p,v;}g[N];
bool cmp(node u,node v) {return u.w<v.w;}
struct xp {ll p,v;}ch[N];
int Q[N],up,ans[N],tmp[N];
ll sp[N],sv[N];
void Update(int x,ll v) {ll pp=x*v;for(;x<=up;x+=x&(-x))sp[x]+=pp,sv[x]+=v;}
ll Sp(int x) {ll sum=0;for(;x;x-=x&(-x))sum+=sp[x];return sum;}
ll Sv(int x) {ll sum=0;for(;x;x-=x&(-x))sum+=sv[x];return sum;}
ll fd_mx(ll p) {
	int l=0,r=up,best=up+1;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(Sp(mid)>=p)best=mid,r=mid-1;
		else l=mid+1;
	}
//	printf("!%d\n",best);
	if(best==up+1) return Sv(up);
	ll P=Sp(best),V=Sv(best);
	ll res=V-(P-p+best-1)/best;
	return res;
}
void solve(int L,int R,int l,int r) {
//	printf("![%d,%d]  [%d,%d]\n",L,R,l,r);
	if(L==R) {for(int i=l;i<=r;i++)ans[Q[i]]=g[L].w;if(g[L].p)Update(g[L].p,g[L].v);return;}
	int mid=(L+R)>>1;
	for(int i=mid+1;i<=R;i++) Update(g[i].p,g[i].v);
	int t1=l,t2=r;
	for(int i=l;i<=r;i++) {
		int x=Q[i];
		ll mxv=fd_mx(ch[x].p);
//		printf("!%d %lld(%lld)\n",x,mxv,ch[x].v);
		if(mxv>=ch[x].v) tmp[t1++]=x;else tmp[t2--]=x;
	}
	for(int i=l;i<=r;i++)Q[i]=tmp[i];
	for(int i=mid+1;i<=R;i++) Update(g[i].p,-g[i].v);
	solve(mid+1,R,l,t2);solve(L,mid,t1,r);
}
int main() {
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d%lld%lld",&g[i].w,&g[i].p,&g[i].v),up=max(up,(int)g[i].p);
	for(int i=1;i<=m;i++) {scanf("%lld%lld",&ch[i].p,&ch[i].v);Q[i]=i;}
	sort(g+1,g+1+n,cmp);g[0]=(node){-1,0,0};
//	for(int i=0;i<=n;i++) printf("%d: %d %lld %lld\n",i,g[i].w,g[i].p,g[i].v);
	solve(0,n,1,m);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}