zoukankan      html  css  js  c++  java
  • 混合果汁:整体二分,线段树

    $Description:$

    小 R 热衷于做黑暗料理,尤其是混合果汁。

    商店里有n种果汁,编号为1~n。i号果汁的美味度是$d_i$,每升价格为$p_i$.小 R 在制作混合果汁时,还有一些特殊的规定,即在一瓶混合果汁中,i号果汁最多只能添加$l_i$升。

    现在有m个小朋友过来找小R要混合果汁喝,他们都希望小 R 用商店里的果汁制作成一瓶混合果汁。
    其中,第m个小朋友希望他得到的混合果汁总价格不大于$g_j$,体积不小于$L_j$。
    在上述这些限制条件下,小朋友们还希望混合果汁的美味度尽可能地高,一瓶混合果汁的美味度等于所有参与混合的果汁的美味度的最小值。请你计算每个小朋友能喝到的最美味的混合果汁的美味度。
    $n,m,p_i,l_i,d_i le 100000,g_j,L_j le 10^12$

    今天刚学整体二分,做道题试试。

    整体二分其实和正常的二分差不多,只不过是离线下来一起二分了而已,所以叫整体二分。

    往往应用与单次$check$复杂度过高的时候。

    通常形式也就是$solve(l,r,L,R)$分别表示答案域以及询问区间。

    然后进行数据结构的修改等,调整至$mid$的状态。然后所有询问利用数据结构看答案是否大于$mid$。

    如果大于$mid$就放进右区间否则放进左区间。递归解决。到了叶节点也就是答案了。

    这道题其实挺模板的。如果知道是整体二分的话。

    直接把每种果汁按照美味度排序,然后二分的答案是下标即可。

    二分的$check$就是看用这个小朋友的钱买任意美味度大于$mid$的果汁,看能否买到足量的果汁。

    在二分之后,果汁之间就没有美味度的区别了,只要大于等于$mid$就行。我们在意的只是价格与数量。

    这样的话我们肯定先买便宜的。要维护支持加入,删除,查询价格排序后的前缀和。

    以价格为下标建一个权值线段树就行,维护饮料总量和总价格。

    如果能买下当前区间所有的就买下来。否则如果买不起左儿子就递归进左儿子,买得起左儿子就递归进右儿子。叶节点特殊处理一下就行了。

    在$solve(l,r,L,R)$时只要把$[mid+1,r]$的果汁加入线段树,$check$所有询问,然后解决左儿子,然后再把这些果汁从线段树里删除,再解决右儿子。

    这样,线段树的总修改次数是区间长度和即$O(n log n)$的。询问次数是问题数乘分治层数也就是$O(q log n)$的。

    总复杂度$O((n+q)log^2 n)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define S 100005
     5 #define lc p<<1
     6 #define rc p<<1|1
     7 #define mid (l+r>>1)
     8 int ans[S],n,q;ll totp[S<<2],totl[S<<2];
     9 struct ps{int p,v;ll l;friend bool operator<(ps a,ps b){return a.v<b.v;}}P[S];
    10 struct qs{ll m,w;int o;}Q[S],rq[S];
    11 void add(int pr,ll L,int p=1,int l=1,int r=100000){
    12     totl[p]+=L;totp[p]+=L*pr;
    13     if(l==r)return;
    14     if(pr>mid)add(pr,L,rc,mid+1,r);
    15     else add(pr,L,lc,l,mid);
    16 }
    17 ll ask(ll m,int p=1,int l=1,int r=100000){
    18     if(m>=totp[p])return totl[p];
    19     if(l==r)return m/l;
    20     if(m>=totp[lc])return totl[lc]+ask(m-totp[lc],rc,mid+1,r);
    21     return ask(m,lc,l,mid);
    22 }
    23 void solve(int l,int r,int ql,int qr){
    24     if(l==r){while(qr>=ql)ans[Q[qr].o]=P[l].v,qr--;return;}
    25     int pl=ql,pr=qr,md=l+r>>1;
    26     for(int i=md+1;i<=r;++i)add(P[i].p,P[i].l);
    27     for(int i=ql;i<=qr;++i)if(ask(Q[i].m)>=Q[i].w)rq[pr--]=Q[i];else rq[pl++]=Q[i];
    28     for(int i=ql;i<=qr;++i)Q[i]=rq[i];
    29     solve(l,md,ql,pl-1);
    30     for(int i=md+1;i<=r;++i)add(P[i].p,-P[i].l);
    31     solve(md+1,r,pl,qr);
    32 }
    33 int main(){
    34     cin>>n>>q;P[0].v=-1;
    35     for(int i=1;i<=n;++i)scanf("%d%d%lld",&P[i].v,&P[i].p,&P[i].l);
    36     for(int i=1;i<=q;++i)scanf("%lld%lld",&Q[i].m,&Q[i].w),Q[i].o=i;
    37     sort(P+1,P+n+1);
    38     solve(0,n,1,q);
    39     for(int i=1;i<=q;++i)printf("%d
    ",ans[i]);
    40 }
    View Code

    然而我写整体二分纯属为了练手。。。

    主席树直接弄不就好了?还少个$log$

    然而我又不是刚学主席树。。。

  • 相关阅读:
    12月11日
    081212 晴
    12月10日
    树莓派项目——基于树莓派的WIFI网络互传系统设计
    IDE
    边缘检测
    Android Launcher桌面应用快捷方式的开发
    android ui事件处理分析
    listview 分析
    ApplicationsIntentReceiver.class
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12304797.html
Copyright © 2011-2022 走看看