首先考虑初始排列,pi会让周围所有比其大的元素深度+1,所以要求每个点的深度,只要其被覆盖了几次即可
这个覆盖可以通过处理每个元素的左右边界(单调栈O(n))+线段树区间更新(Ologn(n))来做
然后是将排列最左边一个元素移到最右边:
在左边删元素pi,只会让pi右边所有比其大的元素深度-1,
在右边加上元素pi,会让pi左边比其大的元素深度+1
这种循环左右移动,需要频繁更改线段树的下标,不好操作,所以我们一开始直接用一个两倍的数组来建立线段树,只要查询时查询长度为n即可
#include#include using namespace std; #define N 400005 int n,a[N],L[N],R[N]; void work(){ stack<int>stk; a[2*n+1]=0; for(int i=1;i<=2*n+1;i++){ if(stk.empty()){stk.push(i);continue;} while(stk.size() && a[stk.top()]>=a[i]){ R[stk.top()]=i-1; stk.pop(); } stk.push(i); } while(stk.size())stk.pop(); a[0]=0; for(int i=2*n;i>=0;i--){ if(stk.empty()){stk.push(i);continue;} while(stk.size() && a[stk.top()]>=a[i]){ L[stk.top()]=i+1; stk.pop(); } stk.push(i); } } #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int lazy[N<<2],Max[N<<2]; void pushdown(int rt){ if(lazy[rt]){ lazy[rt<<1]+=lazy[rt];Max[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt];Max[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } void update(int L,int R,int v,int l,int r,int rt){ if(L<=l && R>=r){Max[rt]+=v;lazy[rt]+=v;return;} int m=l+r>>1; pushdown(rt); if(L<=m)update(L,R,v,lson); if(R>m)update(L,R,v,rson); Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); } int query(int L,int R,int l,int r,int rt){ if(L<=l && R>=r)return Max[rt]; int m=l+r>>1,res=0; pushdown(rt); if(L<=m)res=max(res,query(L,R,lson)); if(R>m)res=max(res,query(L,R,rson)); return res; } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)a[i+n]=a[i]; work();//处理出每个元素可以控制的范围 for(int i=1;i<=n;i++) update(L[i],R[i],1,1,2*n,1); int ans=query(1,n,1,2*n,1),pos=0; for(int i=1;i ){ int s=i,t=i+n; update(L[s],R[s],-1,1,2*n,1);//把s从左端删掉 update(L[t],R[t],1,1,2*n,1);//把t加入右端 int res=query(s+1,t,1,2*n,1); if(res<ans){ ans=res,pos=i; } } cout< " "< '\n'; return 0; }