[CF484E]Sign on Fence
题意
给定一个长度为\(n\)的数列,有\(m\)次询问,询问形如$\ l\ \ r\ \ k\ \( 要你在区间\)[l,r]\(内选一个长度为\)k$的区间,求区间最小值的最大值
sol
二分一个最小值,把大于等于他的设成\(1\),小于他的设成\(0\)。
那就只要查询区间\([l,r]\)中是否存在一个\(1\)的连续段长度超过\(k\)就行了。
按数值从大往小一个个插入主席树,然后就是线段树维护区间最长的\(1\)的连续段。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
struct Data{int v,vl,vr,ful;};
struct president_tree{int ls,rs;Data data;}t[N*50];
int n,m,h[N],id[N],rt[N],tot;
Data operator + (Data a,Data b)
{
Data c;
c.v=max(a.vr+b.vl,max(a.v,b.v));
c.vl=a.vl;if (a.ful) c.vl=a.vl+b.vl;
c.vr=b.vr;if (b.ful) c.vr=b.vr+a.vr;
c.ful=a.ful&b.ful;return c;
}
void modify(int &x,int l,int r,int p)
{
t[++tot]=t[x];x=tot;
if (l==r) {t[x].data=(Data){1,1,1,1};return;}
int mid=l+r>>1;
if (p<=mid) modify(t[x].ls,l,mid,p);
else modify(t[x].rs,mid+1,r,p);
t[x].data=t[t[x].ls].data+t[t[x].rs].data;
}
Data query(int x,int l,int r,int ql,int qr)
{
if (l>=ql&&r<=qr) return t[x].data;
int mid=l+r>>1;
if (qr<=mid) return query(t[x].ls,l,mid,ql,qr);
if (ql>mid) return query(t[x].rs,mid+1,r,ql,qr);
return query(t[x].ls,l,mid,ql,qr)+query(t[x].rs,mid+1,r,ql,qr);
}
bool cmp1(int i,int j){return h[i]>h[j];}
bool cmp2(int i,int j){return i>j;}
int main()
{
n=gi();
for (int i=1;i<=n;++i) h[i]=gi(),id[i]=i;
sort(id+1,id+n+1,cmp1);sort(h+1,h+n+1,cmp2);
for (int i=1;i<=n;++i)
modify(rt[i]=rt[i-1],1,n,id[i]);
m=gi();
while (m--)
{
int l=gi(),r=gi(),k=gi();
int L=1,R=n;
while (L<R)
{
int mid=L+R>>1;
if (query(rt[mid],1,n,l,r).v>=k) R=mid;
else L=mid+1;
}
printf("%d\n",h[L]);
}
return 0;
}