描述
给定n个数据,m个询问,每次询问给出l,r,w,问在区间l到r之间,寻找w个连续数据的,使得它们的最小值最大。
分析
若仅仅一次询问,很明显可以用单调队列来完成,但在这里是多次询问。
首先,找最大值,可以用二分方法,转化为判定[l,r]区间内是否有w个连续数据,最小值为mid;考虑一颗维护区间最大连续和的树,每个位置,若代表的数据大于mid,该位置为1,否则为0,则求该树在[l,r]区间内最大连续字段和即可。
因为结果一定是a[i](1<=i<=n),所以二分查找下标,同时只需要建立n颗树即可,可是建立n棵树的时空消耗太大,但若按照a[i]数值从大到小的顺序建树,每棵树只和前一棵树有一个结点不同,所以排序后按照从大到小建主席树。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")
typedef long long LL;
const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e5+50;
int L[maxn*20],R[maxn*20],lm[maxn*20],rm[maxn*20],ma[maxn*20],len[maxn*20], cur;
int T[maxn];
struct N{
int h,id;
friend bool operator<(const N&a,const N&b){//从小到大;之后用了reverse反转
return a.h<b.h;
}
}a[maxn];
//维护区间最大和
void up(int k){
len[k] = len[L[k]] + len[R[k]];
lm[k] = lm[L[k]]==len[L[k]] ? lm[L[k]]+lm[R[k]] : lm[L[k]];
rm[k] = rm[R[k]]==len[R[k]] ? rm[R[k]]+rm[L[k]] : rm[R[k]];
ma[k] = max(ma[L[k]], ma[R[k]]);
ma[k] = max(ma[k], rm[L[k]]+lm[R[k]]);
}
//建立空树
void build(int &rt,int l,int r){
rt = ++cur;
if(l==r){
lm[rt] = rm[rt] = ma[rt] = 0;
len[rt] = 1;
return;
}
int mid=(l+r)>>1;
build(L[rt], l, mid);
build(R[rt], mid+1, r);
up(rt);
}
//在pre基础上建立新树
void update(int &rt,int pre,int l,int r,int i){
rt = ++cur;
L[rt] = L[pre];
R[rt] = R[pre];
len[rt] = len[pre];
if(l==r){
lm[rt] = rm[rt] = ma[rt] = 1;
return;
}
int mid=(l+r)>>1;
if(i<=mid) update(L[rt], L[pre], l, mid, i);
else update(R[rt], R[pre], mid+1, r, i);
up(rt);
}
//求某棵树上,区间内的最大连续字段和
int ans, lefnow;
void query(int rt,int l,int r,int ll,int rr){ //l,r当前结点范围 ll,rr要求的范围
if(ll<=l && r<=rr){
ans = max(ans, ma[rt]);
lefnow += lm[rt];
ans = max(ans, lefnow);
if(lm[rt]!=len[rt])
lefnow = rm[rt];
return;
}
int mid=(l+r)>>1;
if(mid>=rr) query(L[rt], l, mid, ll, rr);
else
if(mid< ll) query(R[rt], mid+1, r, ll, rr);
else{
query(L[rt], l, mid, ll, rr);
query(R[rt], mid+1, r, ll, rr);
}
}
int main()
{
int n,m;
cin>>n;
for(int i=1; i<=n; i++){
scanf("%d",&a[i].h);
a[i].id = i;
}
sort(a+1, a+1+n);
reverse(a+1, a+1+n);
build(T[0], 1, n);
for(int i=1; i<=n; i++)
update(T[i], T[i-1], 1, n, a[i].id);
cin>>m;
for(int i=1; i<=m; i++){
int l,r,w;
scanf("%d%d%d",&l,&r,&w);
int left=1, right=n, mid, out;
while(left<=right){
mid = (left+right)>>1;
ans = lefnow = 0;
query(T[mid], 1, n, l, r);
if(ans>=w){
out = mid;
right = mid-1;
}
else{
left = mid+1;
}
}
printf("%d\n",a[out].h);
}
}