Description

图片说明

Solution

数据强度可能不够大,不能保证我的写法是完全正确,不过主要是学会用 模拟实现翻转。
对于本题,我们需要找到一个符合条件的 ,随后利用 实现 的翻转。

找到合适的 ,由限制条件 得到启发,最小的不在自己位置的数字一定是最先处理的。不妨找到第一个不在自己位置的数字 ,令
接下来讲一讲如何实现 模拟区间翻转:

  • 维护一个长度为 的双端队列
  • 用一个变量记录翻转次数,如果翻转次数为偶数,在前面插入,否则在后面插入
  • 使用滑动窗口的思想,如果翻转次数为偶数,要从前面插入,所以弹出队尾的数字,否则弹出队首的数字。
  • 最后对于无法翻转的数字再逐一检验。

Code

#include <bits/stdc++.h>
#pragma GCC optimize(3)
typedef long long ll;
using namespace std;

const int N = 4e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

void solve() {
  int n; cin >> n;
  vector<int> a(n + 1, 0), pos(n + 1, 0);
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
    pos[a[i]] = i; // 记录位置
  }  
  int k = 1, cur = 1; 
  for(int i = 1; i <= n; i++) {
    if(pos[i] != i) {
      k = abs(pos[i] - i) + 1; // 找到k, 第一个不在自己位置的数字
      cur = i;
      break;
    }
  }
  deque<int> q;
  for(int i = cur; i <= cur + k - 1; i++) {
    q.push_back(a[i]);
  }
  int flag = 0; // 判断是放前面还是后面
  for(int i = cur; i + k - 1 <= n; i++) {
    // i + k - 1 <= n 可翻转
    if(q.front() != i && q.back() != i) { // 翻转了也实现不了
      cout << "no\n";
      return ;
    }
    if(!flag && q.front() != i) { // 需要翻转
      flag ^= 1;
    } else if(flag && q.back() != i) { // 需要翻转
      flag ^= 1;
    }
    if(!flag) { // 插入队尾
      q.pop_front();
      if(i + k <= n) {
        q.push_back(a[i + k]);
      }
    } else { // 插入队首
      q.pop_back();
      if(i + k <= n) {
        q.push_front(a[i + k]);
      }
    }
  }
  for(int i = n - k + 2; i <= n; i++) { // 最后对于无法翻转的数字再逐一检验。
    int now;
    if(!flag) {
        now = q.front();
        q.pop_front();
    } else {
        now = q.back();
        q.pop_back();
    }
    if(now != i) {
        cout << "no\n";
        return ;
    }
  }
  cout << "yes\n";
  cout << k << '\n';

}
int main() {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  int T = 1; //cin >> T;
  while(T--) {
    solve();
  }
  return 0;
}