F. Out of Sorts II
time limit per test:1 second
memory limit per test:256 megabytes
input:standard input
output:standard output
Keeping an eye on long term career possibilities beyond the farm, Bessie the cow has started learning algorithms from various on-line coding websites.
Her favorite algorithm thus far is “bubble sort”. Here is Bessie’s initial implementation, in cow-code, for sorting an array A of length N.
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
Apparently, the “moo” command in cow-code does nothing more than print out “moo”. Strangely, Bessie seems to insist on including it at various points in her code.
After testing her code on several arrays, Bessie learns an interesting observation: while large elements can be pulled to the end of the array very quickly, it can take small elements a very long time to “bubble” to the front of the array (she suspects this is how the algorithm gets its name). In order to try and alleviate this problem, Bessie tries to modify her code so that it scans forward and then backward in each iteration of the main loop, so that both large and small elements have a chance to be pulled long distances in each iteration of the main loop. Her code now looks like this:
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
Given an input array, please predict how many times “moo” will be printed by Bessie’s modified code.
Input
The first line of input contains N(1≤N≤100,000). The next N lines describe A[0]…A[N−1], each being an integer in the range 0…109. Input elements are not guaranteed to be distinct.
Output
Print the number of times “moo” is printed.
Example
input
5
1
8
5
3
2
output
2
分析
这题题意是是给出一个小于 100,000 个数字且数字大小小于 109 的序列,并求需要进行多少次双向冒泡才能使这个序列有序。
首先需要将输入的数据离散化,不作离散化的数据在排序后很可能是跳跃递增或相同的,这样大概无法建立树状数组,也无法进行模拟:先标出输入数据顺序的下标,如样例的 1 8 5 3 2 的下标记为 1 2 3 4 5 ,并对输入的数据进行排序,使其变为 1 2 3 5 8 。将 1 2 3 5 8 替换成 1 2 3 4 5,再按顺序依据下标还原回 1 5 4 3 2。
还原回去不是必要的,但是这样更易于理解接下去的内容。
因为不能保证输入元素不同,如 1 4 4 3 2 的序列需要变为1 4 5 3 2 ,还需要在排序中在两个元素相同时判断其下标大小,下标越大便排在相同元素的越后面。
然后就是双向冒泡的问题。
如果只是单向冒泡的话可以找出逆序数最大的数字(即该数字前面有几个比它大的数字),但是双向冒泡似乎就不适用了。
可以这样想,每一次向后的冒泡就是把一个大数扔到能到达的最后,向前的冒泡就是把一个小数扔到到达的最前;
我们在序列合适的位置竖起一个标杆,把数组分为前后两部分,使得前面部分比标杆大的大数和后面部分比标杆小的小数最多;想到这种相对于标杆的大小数都是成对出现的,那么每进行一次冒泡,这两个区间就会有一对大小数会对换区间。当区间所有的大小数交换完成时,那序列便有序了;
至于交换完了便有序的原因可以认为是更小尺度内的排序所用到的冒泡排序次数不会超过这个大小数对数最多的数量所需要的情况。(其实我也不懂。)
那么我们只需要从第一个数字开始往后移动这个标杆,同时用树状数组逐个保存数字的位置大小查询大数数量并记录其最大值就可以了。
当然也可以模拟一下这个移动过程,用数组下标的形式来保存访问过的数字就可以解决标杆移动后大数变成小数的问题。
最后注意一下即使序列一开始便有序也需要输出1;
AC代码:
看了其他大佬的保存方式之后稍稍模仿了一下
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define MS(X) memset(X,0,sizeof(X))
#define MSC(X) memset(X,-1,sizeof(X))
#define lowbit(X) (X&(-X))
typedef long long LL;
using namespace std;
const int maxn=1e5+5;
struct node{
int v,num;
} a[maxn];
int cnt[maxn];
bool cmp(node a,node b){
if(a.v!=b.v)
return a.v<b.v;
else
return a.num<b.num;
}
void updata(int x){
for(;x>0;x-=lowbit(x)) cnt[x]++;
}
int query(int x){
int sum=0;
for(;x<=maxn;x+=lowbit(x)) sum+=cnt[x];
return sum;
}
int main(){
int n,ans;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i].v);
a[i].num=i+1;
}
sort(a,a+n,cmp);
for(int i=0;i<n;i++){
a[i].v=i+1;
}
ans=1;
for(int i=0;i<n;i++){
updata(a[i].num);
ans=max(ans,query(i+2));
}
printf("%d\n",ans);
return 0;
}