- 题意:有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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人