题目描述
小敏和小燕是一对好朋友。
他们正在玩一种神奇的游戏,叫Minecraft。
他们正在盖建筑,他们手上有不同的方块。
每种方块有不同的不美观度,他们持有每种方块的数量也不一样。
他们现在准备实行他们众多建筑计划中的一个。
他们的建筑计划有不同的要求,不美观度有上限和下限。为了方便拿取方块,他们也要求所用的方块在方块堆中的连续一段。每个建筑计划需要固定的方块数。
他们现在要知道,在最好情况下,每个建筑计划中最不美观的方块的不美观度是多少。
输入格式
第一行一个整数n,表示方块种类数。
接下来的n 行,第i+1 行有两个整数,pi和qi,分别表示在方块堆中的第i种方块的不美观度和数量。
接下来的一行一个整数m,表示建筑计划的数量。
接下来的m 行,每行五个整数l,r,s,t,k,分别代表该建筑计划的中所用的方块种类要在方块堆的[l,r]区间中,并且美观度要介于[s,t]中,且需要k 个方块。
输出格式
对于每个建筑计划,输出一行一个整数,表示该建筑计划最好情况下最不美观度的方块的不美观度。如果没有满足条件的建筑方案,输出-1。
数据范围
对于30%的数据,n<=10,m<=30
对于40%的数据,n<=100,m<=300
对于60%的数据,n<=1000,m<=3000
对于100%的数据,n<=30000,m<=30000
分析
要求最不美观度最小,可以考虑二分答案。设当前二分到的答案为 m i d mid mid,则问题转化为判定是否在制指定区间内存在 k k k个方块,且不美观度在 l l l到 m i d mid mid之间,若存在,则令 r = m i d r=mid r=mid,否则令 l = m i d + 1 l=mid+1 l=mid+1,继续进行上述二分。最后的答案为 l l l。
现在的问题是如何求区间内小于等于指定数的个数。可以用主席树,即可持久化线段树,以权值为下标对每个值建立由前一个值转移过来的线段树。利用前缀和思想,可以求得在指定区间内小于等于指定数的个数。具体看代码。
由于是按照权值建立的主席树,所以要先离散化。
代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=30005,LogN=20;
int n,m,rt[N],s[N*LogN],nn,L[N*LogN],ans;
int val[N],num[N],tot,R[N*LogN];//s存在当前值域的数的个数
int b[N*3],qs[N],qt[N],ql[N],qr[N],qk[N];
void Build(int &p,int l,int r) {
p=++tot;
if (l==r) return;
int mid=(l+r)>>1;
Build(L[p],l,mid);
Build(R[p],mid+1,r);
}
void Insert(int &p,int rt,int l,int r,int v,int x) {//主席树插入
p=++tot;
s[p]=s[rt];L[p]=L[rt];R[p]=R[rt];
s[p]+=x;
if (l==r) return;
int mid=(l+r)>>1;
if (v<=mid) Insert(L[p],L[rt],l,mid,v,x);
else Insert(R[p],R[rt],mid+1,r,v,x);
}
void Ask(int p,int q,int l,int r,int ql,int qr) {
if (ql<=l&&r<=qr) {//当前值域完全包含与所需值域
ans+=s[q]-s[p];//利用两个值之差得出在当前区间内落在当前值域的数的个数
return;
}
int mid=(l+r)>>1;
if (ql<=mid) Ask(L[p],L[q],l,mid,ql,qr);
if (qr>mid) Ask(R[p],R[q],mid+1,r,ql,qr);
}
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%d%d",&val[i],&num[i]);
b[i]=val[i];
}
nn=n;
scanf("%d",&m);
for (int i=1;i<=m;i++) {
scanf("%d%d%d%d%d",&qs[i],&qt[i],&ql[i],&qr[i],&qk[i]);
b[++nn]=ql[i];
b[++nn]=qr[i];
}
sort(b+1,b+nn+1);//离散化
nn=unique(b+1,b+nn+1)-b-1;
for (int i=1;i<=n;i++) val[i]=lower_bound(b+1,b+nn+1,val[i])-b;
Build(rt[0],1,n);
for (int i=1;i<=n;i++)//一个个在值域中插入数量
Insert(rt[i],rt[i-1],1,nn,val[i],num[i]);
for (int i=1;i<=m;i++) {
ql[i]=lower_bound(b+1,b+nn+1,ql[i])-b;
qr[i]=lower_bound(b+1,b+nn+1,qr[i])-b;
int l=ql[i],r=qr[i]+1,k=qk[i];
int x=qs[i],y=qt[i];
while (l<r) {//二分
int mid=(l+r)>>1;
ans=0;
Ask(rt[x-1],rt[y],1,nn,ql[i],mid);
if (ans>=k) r=mid;
else l=mid+1;
}
if (l>qr[i]) puts("-1");//无解
else printf("%d\n",b[l]);//输出离散化后对应的值
}
return 0;
}