1. 程式人生 > >[Bzoj]5343: [Ctsc2018]混合果汁

[Bzoj]5343: [Ctsc2018]混合果汁

5343: [Ctsc2018]混合果汁


題目描述

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

商店裡有 \(n\) 種果汁,編號為 \(0,1,\cdots,n-1\)\(i\) 號果汁的美味度是 \(d_i\),每升價格為 \(p_i\)。小 R 在製作混合果汁時,還有一些特殊的規定,即在一瓶混合果汁中,\(i\) 號果汁最多隻能新增 \(l_i\) 升。

現在有 \(m\) 個小朋友過來找小 R 要混合果汁喝,他們都希望小 R 用商店裡的果汁製作成一瓶混合果汁。其中,第 \(j\) 個小朋友希望他得到的混合果汁總價格不大於 \(g_j\),體積不小於 \(L_j\)。在上述這些限制條件下,小朋友們還希望混合果汁的美味度儘可能地高,一瓶混合果汁的美味度等於所有參與混合的果汁的美味度的最小值。請你計算每個小朋友能喝到的最美味的混合果汁的美味度。

輸入輸出格式

輸入格式:

輸入第一行包含兩個正整數 \(n, m\),表示果汁的種數和小朋友的數量。接下來 \(n\) 行,每行三個正整數 \(d_i, p_i, l_i\),表示 \(i\) 號果汁的美味度為 \(d_i\),每升價格為\(p_i\),在一瓶果汁中的新增上限為 \(l_i\)

接下來 \(m\) 行依次描述所有小朋友:每行兩個數正整數 \(g_j, L_j\)描述一個小朋友,表示他最多能支付 \(g_j\) 元錢,他想要至少 \(L_j\) 升果汁。

輸出格式:

對於所有小朋友依次輸出:對於每個小朋友,輸出一行,包含一個整數,表示他能喝到的最美味的混合果汁的美味度。如果無法滿足他的需求,則輸出 \(-1\)

說明

對於所有的測試資料,保證 \(n, m \le 100000\)\(1 \le d_i,p_i,l_i \le 10^5, 1 \le g_j, L_j \le 10^{18}\)

測試點編號 \(n=\) \(m=\) 其他限制
1,2,3 \(10\) \(10\)
4,5,6 \(500\) \(500\)
7,8,9 \(5000\) \(5000\)
10,11,12 \(100000\) \(100000\) \(p_i=1\)
13,14,15 \(100000\) \(100000\) \(l_i=1\)
16,17,18,19,20 \(100000\) \(100000\)

題目分析:

考慮大暴力。

假設列舉所有的\(d\),可以發現從大到小答案是單調的。

那麼我們先按照\(d\)排一遍序,然後二分這個\(d\),對於每個d直接暴力尋找是否可以滿足條件,不難發現這個演算法複雜度是\(O(n^2log^2n)\)的。

思考如何優化掉一個\(n\),發現可以對價格建立一棵權值線段樹,然後對於二分的\(d\)就查詢當前的狀態是否能夠滿足總價格小於\(g\),發現這個\(d\)是可持久化的,可以用主席樹實現。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+7;
#define ll long long
#define mid ((l+r>>1))
struct Node{
    int d,p,l;
    bool operator <(const Node &rhs)const{return d<rhs.d;}
}a[MAXN];
struct Segtree{
    ll v,w;
}st[MAXN<<5];
int L[MAXN<<5],R[MAXN<<5],T[MAXN],size;
int n,m;
ll g,lim;
inline ll read()
{
    ll x=0,c=1;char ch=' ';
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    while(ch=='-') c*=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return c*x;
}
int update(int pre,int l,int r,int x,int v,ll w)
{
    int rt=++size;
    L[rt]=L[pre],R[rt]=R[pre],st[rt].v=st[pre].v+v,st[rt].w=st[pre].w+w;
    if(l==r) return rt;
    if(x<=mid) L[rt]=update(L[pre],l,mid,x,v,w);
    else R[rt]=update(R[pre],mid+1,r,x,v,w);
    return rt;
}
ll query(int v,int l,int r,ll x)
{
    if(l==r) return (ll)x*l;
    if(st[L[v]].v>=x) return query(L[v],l,mid,x);
    else return st[L[v]].w+query(R[v],mid+1,r,x-st[L[v]].v);
}
ll solve()
{
    g=read();lim=read();
    if(lim>g) return -1;
    int l=0,r=n,ans=0;
    while(l<=r){
        ll Mid=l+r>>1;
        ll d=query(T[Mid],1,MAXN,lim);
        if(lim<=st[T[Mid]].v&&d<=g) ans=Mid,l=Mid+1;
        else r=Mid-1;
    }
    return a[ans].d;
}
int main()
{
//  freopen("data.in","r",stdin);
//  freopen("my.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i].d=read();a[i].p=read();a[i].l=read();
    }
    sort(a+1,a+n+1);
    a[0].d=-1;
    T[n+1]=++size;
    for(int i=n;i>=1;i--){
        T[i]=update(T[i+1],1,MAXN,a[i].p,a[i].l,(ll)a[i].l*a[i].p);
    }
    T[0]=T[1];
    while(m--)  printf("%lld\n",solve());
    return 0;
}