之前GDOI的时候了解过,随机数据的用途就是在其中找一段单调上升/下降的子序列期望长度为logn
然而这道题还是不懂怎么用的说。。
首先考虑对于i<j<k,若ci>cj>ck则对于k来说i是无用的。
也就是说对于当前点k,我们需要找到一个后往前单调下降的子序列并且这些数都比c[k]大
对于比c[k]小的我们同理找一个单调上升的子序列
具体做法就是枚举右端点,对于前面找一个后往前单调下降并且这些数都比c[k]大的的子序列,然后一一更新这些点的最小值。离散值作为线段树的下标,利用线段树找区间最大的原下标就可以logn找到下一个位置。对于答案按右端点排序,求区间内每个点的最小值即可。
维护复杂度是nlog^2n的,询问是Qlogn
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int c[110000],lslen,ls[110000],g[110000]; struct query{int l,r,id;}q[110000];int as[110000]; bool cmp(query q1,query q2){return q1.r<q2.r;} //---------------------------------------------------------------------- struct trnode { int l,r,lc,rc; int c; int v; }tr[210000];int trlen; void bt(int l,int r) { int now=++trlen; tr[now].l=l;tr[now].r=r; tr[now].lc=tr[now].rc=-1; tr[now].c=(1<<30); tr[now].v=0; if(l<r) { int mid=(l+r)/2; tr[now].lc=trlen+1;bt(l,mid); tr[now].rc=trlen+1;bt(mid+1,r); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int mn[110000]; void change(int now,int x,int c) { if(tr[now].l==tr[now].r){tr[now].c=c;return ;} int mid=(tr[now].l+tr[now].r)/2; int lc=tr[now].lc,rc=tr[now].rc; if(x<=mid)change(lc,x,c); else change(rc,x,c); tr[now].c=min(tr[lc].c,tr[rc].c); } int getmin(int now,int l,int r) { if(tr[now].l==l&&tr[now].r==r)return tr[now].c; int mid=(tr[now].l+tr[now].r)/2; int lc=tr[now].lc,rc=tr[now].rc; if(r<=mid) return getmin(lc,l,r); else if(mid+1<=l)return getmin(rc,l,r); else return min(getmin(lc,l,mid),getmin(rc,mid+1,r)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~带修求区间最小~~~~~~~~~~~~~~~~~~~~~~~~~ void add(int now,int x,int v) { if(tr[now].l==tr[now].r){tr[now].v=v;return ;} int mid=(tr[now].l+tr[now].r)/2; int lc=tr[now].lc,rc=tr[now].rc; if(x<=mid)add(lc,x,v); else add(rc,x,v); tr[now].v=max(tr[lc].v,tr[rc].v); } int FindCloser(int now,int l,int r) { if(l>r)return 0; if(tr[now].l==l&&tr[now].r==r)return tr[now].v; int mid=(tr[now].l+tr[now].r)/2; int lc=tr[now].lc,rc=tr[now].rc; if(r<=mid) return FindCloser(lc,l,r); else if(mid+1<=l)return FindCloser(rc,l,r); else return max(FindCloser(lc,l,mid),FindCloser(rc,mid+1,r)); } //~~~~~~~~~~~~~~~~找到第一个比当前点大/小的点(维护单调性)~~~~~~~~~~~~~~~~~~~~~~~~ //------------------------------------------------------------------ int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n,Q; scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++) scanf("%d",&c[i]), ls[++lslen]=c[i]; sort(ls+1,ls+lslen+1); lslen=unique(ls+1,ls+lslen+1)-ls-1; for(int i=1;i<=n;i++) g[i]=lower_bound(ls+1,ls+lslen+1,c[i])-ls; for(int i=1;i<=Q;i++) scanf("%d%d",&q[i].l,&q[i].r), q[i].id=i; sort(q+1,q+Q+1,cmp); int tp=1,u; trlen=0;bt(1,n); memset(mn,63,sizeof(mn)); for(int i=1;i<=n;i++) { for(int k=FindCloser(1,g[i]+1,lslen);c[k]>c[i]&&k;k=FindCloser(1,g[i]+1,g[k]-1))//FindFirstMax if(mn[k]>c[k]-c[i]) mn[k]=c[k]-c[i], change(1,k,mn[k]); for(int k=FindCloser(1,1,g[i]-1);c[i]>c[k]&&k;k=FindCloser(1,g[k]+1,g[i]-1))//FindFirstMin if(mn[k]>c[i]-c[k]) mn[k]=c[i]-c[k], change(1,k,mn[k]); add(1,g[i],i); while(tp<=Q&&q[tp].r==i) { as[q[tp].id]=getmin(1,q[tp].l,q[tp].r); tp++; } } for(int i=1;i<=Q;i++)printf("%d ",as[i]); return 0; }