[USACO18OPEN]Out of Sorts G

描述

传送门:P4375

传送门:UPC6347

留意着农场之外的长期职业生涯的可能性,奶牛Bessie开始在不同的在线编程网站上学习算法。

她到目前为止最喜欢的算法是“冒泡排序”。这是Bessie的对长度为 $N$ 的数组 $A $进行排序的奶牛码实现。

1
2
3
4
5
6
7
8
sorted = false
while (not sorted):
sorted = true
moo
for i = 0 to N-2:
if A[i+1] < A[i]:
swap A[i], A[i+1]
sorted = false

显然,奶牛码中的“moo”指令的作用只是输出“moo”。奇怪的是,Bessie看上去执着于在她的代码中的不同位置使用这个语句。

在用若干个数组测试了她的代码之后,Bessie得到一个有趣的观察现象:大的元素很快就会被拉到数组末尾,然而小的元素需要很长时间“冒泡”到数组的开头(她怀疑这就是为什么这个算法得名的原因)。为了实验和缓解这一问题,Bessie试着修改了她的代码,使代码在每次循环中向前再向后各扫描一次,从而无论是大的元素还是小的元素在每一次循环中都有机会被拉较长的一段距离。她的代码现在是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
sorted = false
while (not sorted):
sorted = true
moo
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

给定一个输入数组,请预测Bessie的代码会输出多少次“moo”。

输入

输入的第一行包含 $N$ ( 1≤N≤100,0001≤N≤100,000 )。接下来 $N$行描述了 $A[0]…A[N−1]$ ,每个数都是一个范围为 0… $10^9$的整数。输入数据不保证各不相同。

输出

输出“moo”被输出的次数。

样例

输入

5
1
8
5
3
2

输出

2

思路

  1. 首先,每一次while()循环都会有两个跑错位置的数回到自己应该在的位置
    • 以样例为例
    • 1 8 5 3 2
    • 1 2 5 3 8 <—- 一次 while()循环之后 8跟2回到了自己应该在的位置
    • 1 2 3 5 8 <—- 两次while()循环之后
  2. 我们定义一个名词“分割线”(恰好将一半跑到前边的大数跟一半跑到后边的大数分隔开)
    • 例如:样例中的$5$和$3$中间
    • 分割线所在的位置在多次while()循环时一直是固定的(可以自己模拟几次试试)
    • $注意:分割线的位置我们是不知道具体在哪的,并且也不需要求出来它在哪,我们只需要遍历一遍就好了$
    1. 然后我们将输入的数据进行离散化,用a[i].id来表示数据a[i].x应该在的位置
    2. 离散化完成后进行以下几步
      • 遍历一遍
      • 遍历的同时将$a[i].id$加到树状数组中,$bit[x]$表示$[1,x]$中有几个站对位置的数
      • $sum(i)$求的是在$[1,i]$中有多少站对位置的数
      • $i-sum(i)$表示$[1,i]$中有多少没站对位置的数
      • $站对位置:本应在分割线后边的数老老实实呆在后边$
      • $本应在后边的数跑到前边来便是没站对位置$
      • 取$i-sum(i)$的最大值
  3. 特殊情况:不需要排序
    • 有可能所有的数全部相等($不保证没有重复的数$)
    • 本身有序($1$ $2$ $3$ $ 4$ $5$)
      $特殊情况下while()循环运行的最小次数仍为1,用max(ans,1)即可解决$
对此题的理解仍不透彻,以上题解可能有错,如有错误欢迎指出,谢谢!!!

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
* =========================================================================
*
* Filename: E.cpp
*
* Link: https://www.luogu.org/problemnew/show/P4375
*
* Version: 1.0
* Created: 2018/07/22 12时21分29秒
* Revision: none
* Compiler: g++
*
* Author: 杜宁元 (https://duny31030.github.io/), duny31030@126.com
* Organization: QLU_浪在ACM
*
* =========================================================================
*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define max3(a,b,c) fmax(a,fmax(b,c))
#define ios ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int N = 100010;
int bit[N+1]; // bit[i]表示排好序之后有多少个数在[1,i]内
int n;
struct node
{
int x,id;
}a[N+1];

void add(int i,int x)
{
while(i <= n)
{
bit[i] += x;
i += (i&-i);
}
}

int sum(int i)
{
int s = 0;
while(i > 0)
{
s += bit[i];
i -= (i&-i);
}
return s;
}

bool cmp(const node a,const node b)
{
return a.x < b.x || (a.x == b.x && a.id < b.id);
}

int main()
{
ios
#ifdef ONLINE_JUDGE
#else
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
int ans = -99;
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> a[i].x;
a[i].id = i;
}
sort(a+1,a+n+1,cmp);
for(int i = 1;i <= n;i++)
{
add(a[i].id,1);
ans = max(ans,i-sum(i));
}
printf("%d",max(ans,1));
fclose(stdin);
// fclose(stdout);
return 0;
}