[ USACO 2018 OPEN ] Out of Sorts (Gold)

\(\\\)

\(Description\)


运行以下代码对一长为\(N\)的数列\(A\)排序,不保证数列元素互异:

cnt = 0
sorted = false
while (not sorted):
   cnt = cnt + 1
   sorted = true
   for i = 0 to N-2:
      if A[i+1] < A[i]: swap A[i], A[i+1]
   for i = N-2 downto 0:
      if A[i+1] < A[i]: swap A[i], A[i+1]
   for i = 0 to N-2:
      if A[i+1] < A[i]: sorted = false

求退出循环后\(cnt\)的值。

  • \(N\in [0,10^5]\)\(A_i\in [0,10^9]\)

\(\\\)

\(Solution\)


  • 求双向冒泡排序操作次数就没有\(silver\)那道题那么简单了,因为向前移动的瓶颈被去掉了,而换做局部的移动限制。

  • 转化问题。设\(t_i\)表示将区间\([1,i]\)变为排序后状态的最少操作次数,那么答案即为\(max\{t_i\}\)。设\(s_i\)排序后在\([1,i]\)中而原数列里不在\([1,i]\)的元素个数,可以证明\(s_i=t_i\)。因为双向冒泡排序可以保证,每次循环将当前未归位的最大元素和未归位的最小元素归位,所以对于一个没有完成排序的前缀区间,每次会将一个不应在该区间里的元素移出,将一个应该在改区间却原来不在的元素归位。

  • 具体实现可以对下标开树状数组,将排序后的数列按顺序插入每个元素的初始位置,用总个数减前缀和或后缀和的方式查询即可。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define R register
#define gc getchar
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,ans=1;
struct seq{int x,p;}s[N];

struct BIT{
  int c[N];
  inline int lowbit(int x){return x&-x;}
  inline void add(int p){for(;p<=n;p+=lowbit(p)) ++c[p];}
  inline int sum(int p){
    int res=0;
    for(;p;p-=lowbit(p)) res+=c[p];
    return res;
  }
}bit;

inline bool cmp(seq x,seq y){
  return x.x==y.x?x.p<y.p:x.x<y.x;
}

int main(){
  n=rd();
  for(R int i=1;i<=n;++i){s[i].x=rd();s[i].p=i;}
  sort(s+1,s+1+n,cmp);
  for(R int i=1;i<=n;++i){
    bit.add(s[i].p);
    ans=max(ans,i-bit.sum(i));
  }
  printf("%d\n",ans);
  return 0;
}

posted @ 2018-09-17 14:16  SGCollin  阅读(319)  评论(0编辑  收藏  举报