都说是$CTSC2018$最水的题。。。还是太蒻了想了很长时间。。。$QAQ$
考虑一下朴素的解法:
对$d_i$排序,对$d_i$二分答案。因为答案取的是最小值,所以比它大的随便加。$d_i$越小能加的范围就越大,答案具有单调性,因此可以二分。然后尽可能把所有较小的$p_i$加上,看看加到最大后$\sum l_i$是否满足,以此判断二分范围。
复杂度$O(n^2logn)$
瓶颈在于选取尽可能多得选取较小的$p_i$。可以发现它能用主席树维护。首先按$p_i$排序,获取每种果汁$p_i$的排名;再按$d_i$排序,依次按照$p_i$的排名插入主席树,每个节点维护一下$\sum p_i*l_i$(把这些果汁全加上的价格)和$\sum l_i$(这些果汁的总容量)。
查询时,尽可能把左儿子选上,也就是只要$\sum p_i*l_i \le g_j$就把总容量加上$\sum l_i$,$g_j$减去$\sum p_i*l_i$,走右儿子。当然可能出现递归到叶子节点,剩余的$g_j$不足以全加上这种果汁,尽可能多加,即返回$min\{g_j/p_i,l_i\}$。
总之都是基于贪心思想。其他过程还是和开始的做法一样。时间复杂度$O(nlog^2n)$
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 100005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
template<typename T>
inline T read(){
T x=0;
int y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
int root[maxn];
struct Drink{
int d,l,num;
long long p;
}d[maxn];
struct Chairman_Tree{
int ls[maxn<<5],rs[maxn<<5],cnt;
long long sum[maxn<<5],con[maxn<<5],p[maxn<<5];
//sum是li*pi的和,con是li的和,p是叶子节点的pi
#define ls(x) ls[x]
#define rs(x) rs[x]
void build(int poi,int l,int r,int &node,int ol,long long L,long long P){
node=++cnt;
sum[node]=sum[ol]+L*P;
con[node]=con[ol]+L;
if(l==r){
p[node]=P;
return;
}
int mid=l+r>>1;
if(poi<=mid)rs(node)=rs(ol),build(poi,l,mid,ls(node),ls(ol),L,P);
else ls(node)=ls(ol),build(poi,mid+1,r,rs(node),rs(ol),L,P);
}
long long ask(int l,int r,int node,long long g){
if(l==r)return node?min((g/p[node]),con[node]):0;
//空节点注意判断一下,否则会出现除以0的情况
int mid=l+r>>1;
if(sum[ls(node)]>g)return ask(l,mid,ls(node),g);
//剩下的钱不足以全加上,走左儿子
else return ask(mid+1,r,rs(node),g-sum[ls(node)])+con[ls(node)];
//付得起,贪心全加上,走右儿子
}
}ct;
inline bool cmp1(Drink x,Drink y){
return x.p<y.p;
}
inline bool cmp2(Drink x,Drink y){
return x.d>y.d;
}
int main(){
int n=read(),m=read();
for(register int i=1;i<=n;++i)
d[i].d=read(),d[i].p=read<long long>(),d[i].l=read();
sort(d+1,d+1+n,cmp1);//按pi排序
for(register int i=1;i<=n;++i)
d[i].num=i;//获取排名
sort(d+1,d+1+n,cmp2);//按di排序
for(register int i=1;i<=n;++i)
ct.build(d[i].num,1,n,root[i],root[i-1],d[i].l,d[i].p);
//以pi的排名建树
int l,r;
while(m--){
long long g=read<long long>(),L=read<long long>();
int ans=-1;
l=1,r=n;
while(l<=r){
int mid=l+r>>1;
long long k=ct.ask(1,n,root[mid],g);
if(k>=L)ans=d[mid].d,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
}