题意:
给定一个长度为N的物品排列和一个可以把一段连续的物品翻转的机械臂,你要通过N次机械臂操作将物品按升序排序,第i次操作必须将第i个物品放在第i个位置上。即在第i次操作中,你需要用机械臂将区间[i,P[i]]翻转(P[i]为该次操作前第i个数的位置)。你在每次操作前需要输出P[i]。
简化版:你需要维护一个序列,支持区间翻转与查询区间最小。
题解:
①由于区间最小实际上每一次就是对应的整个数列的第k小,因此可以直接预处理解决,接下来考虑如何找到这个点,可以直接用一个指针解决,然后就是简单的无旋treap操作:
②给定一个平衡树上节点,求它在当前序列中的下标,首先我们先将这个点到平衡树根节点的标记下传,使用递归解决,然后就直接根据BST的性质查找即可。
③其余的就是按照题意进行区间rotate,这是无旋Treap的简单操作之一,不多赘述。
#pragma GCC optimize (4) #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=100000+10; const int Inf=1e9; typedef long long LL; int n,A[maxn],num,root; struct Hash{ int id,x; bool operator<(const Hash &A)const{ if(A.x==x)return id<A.id; return x<A.x; } }Map[maxn]; struct Treap{ int lch,rch; int data,Min,size; bool lazy; #define L(u) a[u].lch #define R(u) a[u].rch }a[maxn<<1]; typedef pair<int,int> D; void Init(); void Pushup(int); void Rotate(int); void Pushdown(int); int Build(int,int); D Split(int,int); int Merge(int,int); int GetRank(int); int main(){ a[0].Min=Inf; Init(); return 0; } void Init(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&A[i]),Map[i].x=A[i],Map[i].id=i; sort(Map+1,Map+1+n); for(int i=1;i<=n;i++) A[Map[i].id]=i; root=Build(1,n); for(int i=1;i<=n;i++){ int tmp=GetRank(i); printf("%d ",tmp+i-1); D y=Split(root,tmp); Rotate(y.first); root=Merge(y.first,y.second); root=Split(root,1).second; } } void Pushup(int u){ a[u].Min=min(a[L(u)].Min,a[R(u)].Min); a[u].Min=min(a[u].Min,a[u].data); a[u].size=a[L(u)].size+a[R(u)].size+1; } void Rotate(int u){ a[u].lazy^=1; swap(L(u),R(u)); } void Pushdown(int u){ a[u].lazy=0; if(L(u))Rotate(L(u)); if(R(u))Rotate(R(u)); } int Build(int left,int right){ if(left>right)return 0; int mid=(left+right)>>1; int u=++num; a[u].lazy=0; a[u].data=A[mid]; a[u].Min=Inf; L(u)=Build(left,mid-1); R(u)=Build(mid+1,right); Pushup(u); return u; } D Split(int u,int k){ if(u==0)return D(0,0); if(a[u].lazy)Pushdown(u); D y; int num=a[L(u)].size; if(num>=k){ y=Split(L(u),k); L(u)=y.second; Pushup(u); y.second=u; }else{ y=Split(R(u),k-num-1); R(u)=y.first; Pushup(u); y.first=u; } return y; } int Merge(int A,int B){ if(A==0)return B; if(B==0)return A; if(a[A].lazy)Pushdown(A); if(a[B].lazy)Pushdown(B); if(rand()%(a[A].size+a[B].size)<a[A].size){ a[A].rch=Merge(a[A].rch,B); Pushup(A); return A; } else{ a[B].lch=Merge(A,a[B].lch); Pushup(B); return B; } } int GetRank(int x){ int u=root; int sum=0; while(1){ if(a[u].data==x)return sum+a[L(u)].size+1; if(a[u].lazy)Pushdown(u); if(a[L(u)].Min==x)u=L(u); else sum+=a[L(u)].size+1,u=R(u); } }