牛牛与交换排序(deque的手写和lazy标记翻转)

https://ac.nowcoder.com/acm/contest/9982/F


牛牛有一个数组,数组元素是1到n的排列,即数组的值在1~n范围内,且每个数字仅出现1次。
牛牛想要将该数组变为升序排列的,他可以进行如下的操作。

首先他要确定一个长度k,k的范围在1~n之间。
接下来他将会进行若干次操作。在每轮操作中他都可以选择一段长度为k的子数组,然后进行区间的翻转操作。

他可以做任意次数的操作,但是要求他每次选择的子数组区间满足li​≤li+1​,并且区间长度等于一开始选定的k,也就是说一旦某一次操作选择了数组的某个位置进行区间翻转操作,下一次做区间翻转的位置将会比上一次更靠右。

牛牛发现,并不总是存在一个k可以使得数组排序变为有序,请你告诉牛牛是否存在一个k能够在满足规则的情况下完成排序。

 


思路:可以发现找到一个和位置不对应的点就是k的答案。根据这个k去模拟,遇到不对的看一下翻转的对否就行。不对就gg。

至于翻转,reverse是O(n)的,这么干肯定不行。经常有翻转打lazy标记的。这题算个小trick.

deque是双端的,通过lazy标记可以知道当前应该从队头出还是队尾出,达到o(1)reverse的效果。

如果stl的deque常数大,可以手写。

附上出题人手写的deque板子。

struct deQue
{
    int buffer[MAXN*2];
    int head=MAXN,tail=MAXN-1;
    bool rev;
    bool empty()
    {
        return tail<head;
    }
    int size()
    {
        return tail-head+1;
    }
    int front()
    {
        return rev?buffer[tail]:buffer[head];
    }
    int back()
    {
        return rev?buffer[head]:buffer[tail];
    }
    void push_front(int x)
    {
        rev?buffer[++tail]=x:buffer[--head]=x;
    }
    void push_back(int x)
    {
        rev?buffer[--head]=x:buffer[++tail]=x;
    }
    void pop_back()
    {
        rev?head++:tail--;
    }
    void pop_front()
    {
        rev?tail--:head++;
    }
    void reverse()
    {
        rev^=1;
    }
}q;
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<deque>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL a[maxn],pos[maxn];
deque<LL>que;
LL revtag=0;///表示正常的左边是队头
void pushbacknum(LL revtag,LL num){
    if(revtag==0) que.push_back(num);
    if(revtag==1) que.push_front(num);
}
LL getfront(LL revtag){///获得队首元素
    if(revtag==0) return que.front();
    if(revtag==1) return que.back();
    return 0;
}
void popfront(LL revtag){///弹出队首元素
    if(revtag==0) que.pop_front();
    if(revtag==1) que.pop_back();
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<=n;i++){
    cin>>a[i];
    pos[a[i]]=i;
  }
  bool flag=1;LL k=0;
  for(LL i=1;i<=n;i++){
    if(a[i]!=i){
        k=(pos[i]-i+1);
        flag=0;
        break;
    }
  }
  if(flag==1){
    cout<<"yes"<<"\n"<<1<<"\n";
    return 0;
  }
  flag=1;
  for(LL i=1;i<=k;i++){
    pushbacknum(revtag,a[i]);
  }
  for(LL i=1;i<=n;i++){
   /// cout<<"i="<<i<<" "<<"队首元素为:"<<getfront(revtag)<<'\n';
    if(getfront(revtag)!=i&&que.size()==k) revtag^=1;///当前不对应,反转一下
    if(getfront(revtag)!=i){
        ///cout<<"i="<<i<<" "<<"队首元素为:"<<getfront(revtag)<<'\n';
        flag=0;
        break;
    }
    popfront(revtag);
    if(i+k<=n) {
        pushbacknum(revtag,a[i+k]);
    ///    cout<<"加入:"<<a[i+k]<<'\n';
    }
  }
  if(flag==1){
    cout<<"yes"<<"\n";
    cout<<k<<"\n";
  }
  else if(flag==0) cout<<"no"<<"\n";
return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值