接下来就要开始了,听说这是个黑题,我尽量好好写。请准备一下纸和笔(不用)。。。。。
首先引理1:两个优美区间的交集是优美区间,不含包含的情况,这个应该手模一下就能证明。。。
引理2:定义一个区间的num就是区间内数值相差1的点对数,那么得到优美区间的等价条件就是l+num=r这个可以理解一下,例如1 7 6的num就是1(对),则1+1<3因此不是优美区间,而5 7 6的num是2则1+2=3所以是优美。证明的话我们可以考虑把序列排序,num就代表从最小值开始加,每次只能加1,能加多少次的,那么在例1中只能连续加一次,例2可以连续加2次就是加到头,这就是优美区间。
引理3:k+num(k,i)<=i,这个是显然的,点对不能超过区间长度
然后就是我们的结论:从r枚举优美区间的右端点,第一个能够包住[l,r]的优美区间就是答案
于是上图就出现了,假如我枚举r1算到了l1,然后l2和r2其实是最优解怎么办,但是这种东西是不存在的,因为你由引理1可以知道[l2,r1]也是一个优美区间,那么枚举r1是直接得到l2的,而非l1。
当然我们不是对没一个区间做一个这样的枚举,那样铁定炸飞,我们应该像CDQ一样,对一堆区间做一遍枚举,信息尽量重复利用。那么我们的思路逐渐清晰,就是要枚举每一个i然后处理所有右端点在i之前的没被回答的询问。
但是好像又遇到了问题,假设在枚举一个i的时候,我要得到的信息是怎样的,当我要处理询问[l,r]时,我要得到一个最靠右的k使得,k(属于)[1,l],并且k+num(k,i)=i(要找到一个优美区间[k,i]并且能抱住[l,r])。然后就死了,这**怎么用线段树维护啊。。。
挤细西考一下,有引理3,那么就可以搞了,维护一个k+num(k,i)的最大值及其位置,每次直接查(1,l)中的最大值val,要么=i,要么<i,如果val=i显然合法。那么每次枚举到一个新的点i,就要更新一下区间里的所有点的num,把num(k,i)变成num(k,i+1),其实这个很好维护,就是对于i,和它值差1的就2个,分别是a[i]+1和a[i]-1,它们的位置是pos[a[i]-1],pos[a[i]+1],如果一个pos出现在i之前那么这个i和pos就能组成一对点,所有的1<k<pos都有num(k,i)要+1,因为在1-pos之间的k到i的点对显然多了一对i和pos。
整理下思路,每次把枚举一对点,把我现在能处理的区间,就是右端点等于i的放到一个待回复集合中,让它左端点有序,原因是如果出现不合法能够快速跳走。我把询问的所有l从大到小排序,直接得到1,l中的k+num(k,i)最大值及最靠右的k,如果小于i那么1,l中任意一个数和i组成的区间都无法满足,剩下一堆区间直接不用算,因为更小的k绝对不可能查出来更大的k+num(k,i),此时一定无法满足,只能等到下一个i才可能满足。然后就结束了。用个堆或者set都行我不会用set。然而某wmz以及二营长还有mikufun都没素质卡分块调块长直接碾标算。。听说还有猿小鲲。。
#include<iostream> #include<queue> #include<cstdio> #include<vector> using namespace std; const int N=100020; int a[N],pos[N],ans[N][2],tt,n,m; struct tree{int l,r,w,p,f;}tr[N*4]; struct node { int l,r,id; bool friend operator <(node a,node b) {return a.l<b.l;} }; struct pub{int w,p;}; vector<node> v[N]; priority_queue<node> q; inline int rd() { int s=0,w=1; char cc=getchar(); while(cc<'0'||cc>'9') {if(cc=='-')w=-1;cc=getchar();} while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); return s*w; } inline void change(const register int k,const register int w) { tr[k].w+=w; tr[k].f+=w; } inline pub merge(const register pub a,const register pub b) { pub c; c.w=max(a.w,b.w); if(a.w>b.w) c.p=a.p; else c.p=b.p; return c; } inline void updata(const register int k) { tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w); if(tr[k<<1].w>tr[k<<1|1].w) tr[k].p=tr[k<<1].p; else tr[k].p=tr[k<<1|1].p; } inline void down(const register int k) { change(k<<1,tr[k].f);change(k<<1|1,tr[k].f); tr[k].f=0; } void build(const register int k,const register int l,const register int r) { tr[k].l=l;tr[k].r=r;tr[k].f=0; if(l==r){tr[k].p=tr[k].w=l;return;} const register int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); updata(k); } void add(const register int k,const register int x,const register int y) { int l=tr[k].l,r=tr[k].r,mid=l+r>>1; if(l==x&&r==y){change(k,1);return;} if(tr[k].f) down(k); if(y<=mid) add(k<<1,x,y); else if(x>mid) add(k<<1|1,x,y); else add(k<<1,x,mid),add(k<<1|1,mid+1,y); updata(k); } pub ask(const register int k,const register int x,const register int y) { int l=tr[k].l,r=tr[k].r,mid=l+r>>1; if(l==x&&r==y) return (pub){tr[k].w,tr[k].p}; if(tr[k].f) down(k); if(y<=mid) return ask(k<<1,x,y); else if(x>mid) return ask(k<<1|1,x,y); return merge(ask(k<<1,x,mid),ask(k<<1|1,mid+1,y)); } int main() { n=rd(); build(1,1,n); for(int i=1;i<=n;i++) a[i]=rd(),pos[a[i]]=i; m=rd(); for(int i=1;i<=m;i++) { int l=rd(),r=rd(); v[r].push_back((node){l,r,i}); } for(int i=1;i<=n;i++) { if(a[i]-1>=1&&pos[a[i]-1]<i)add(1,1,pos[a[i]-1]); if(a[i]+1<=n&&pos[a[i]+1]<i)add(1,1,pos[a[i]+1]); for(int j=0;j<v[i].size();j++) q.push(v[i][j]); while(!q.empty()) { node tmp=q.top(); pub temp=ask(1,1,tmp.l); if(temp.w!=i) break; else ans[tmp.id][0]=temp.p,ans[tmp.id][1]=i,q.pop(); } } for(int i=1;i<=m;i++)printf("%d %d ",ans[i][0],ans[i][1]); } /* g++ 1.cpp -o 1 ./1 7 3 1 7 5 6 4 2 3 3 6 7 7 1 3 */