首先一个非常重要的性质是,两个好的区间的交依然是好的区间。
有了这个性质,我们只要找到包含某个区间的右端点最小的好区间,然后就是这个区间的答案拉。
至于找右端点最小的好区间就是一个扫描线问题啦 (和我之前出的那个题有点像,只不过从树上放到了序列上)
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=100005; #define pb push_back #define lc (o<<1) #define mid (l+r>>1) #define rc ((o<<1)|1) inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x; } void W(int x){ if(x>=10) W(x/10); putchar(x%10+'0');} struct ask{ int L,num; bool operator <(const ask &u)const{ return L<u.L; } }; vector<ask> g[N]; multiset<ask> s; multiset<ask> :: iterator it; int n,a[N],p[N],mx[N*4],pos[N*4],Q; int le,ri,w,al[N],ar[N],M,P,tag[N*4]; inline void mt(int o){ mx[o]=max(mx[lc],mx[rc]); pos[o]=(mx[rc]==mx[o]?pos[rc]:pos[lc]); } inline void work(int o,int der){ tag[o]+=der,mx[o]+=der;} inline void pd(int o){ if(tag[o]) work(lc,tag[o]),work(rc,tag[o]),tag[o]=0;} void build(int o,int l,int r){ mx[o]=r,pos[o]=r; if(l==r) return; build(lc,l,mid),build(rc,mid+1,r); } void update(int o,int l,int r){ if(l>=le&&r<=ri){ work(o,w); return;} pd(o); if(le<=mid) update(lc,l,mid); if(ri>mid) update(rc,mid+1,r); mt(o); } void query(int o,int l,int r){ if(l>=le&&r<=ri){ if(mx[o]>M) M=mx[o],P=pos[o]; return;} pd(o); if(ri>mid) query(rc,mid+1,r); if(le<=mid) query(lc,l,mid); } inline bool can(int cc){ le=1,ri=it->L,M=P=0; query(1,1,n); if(M==cc){ al[it->num]=P,ar[it->num]=cc; return 1;} return 0; } inline void solve(){ build(1,1,n),w=1; ask inf=(ask){233333,0}; for(int i=1;i<=n;i++){ for(ask x:g[i]) s.insert(x); ri=p[a[i]-1],le=1; if(ri&&ri<i) update(1,1,n); ri=p[a[i]+1],le=1; if(ri&&ri<i) update(1,1,n); for(;s.size();s.erase(it)){ it=--s.lower_bound(inf); if(!can(i)) break; } } } int main(){ // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); n=read(); for(int i=1;i<=n;i++) p[a[i]=read()]=i; Q=read(); for(int i=1,l,r;i<=Q;i++){ l=read(),r=read(); g[r].pb((ask){l,i}); } solve(); for(int i=1;i<=Q;i++) W(al[i]),putchar(' '),W(ar[i]),puts(""); return 0; }