codeforces A. Out of Sorts(树状数组)

A. Out of Sorts

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 implementation, in cow-code, for sorting an array AA of length NN.

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.

Given an input array, please predict how many times “moo” will be printed by Bessie’s code.

Input

The first line of input contains N (1≤N≤100,000). The next NN 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
5
3
8
2
output
4

分析

之前写过这道题的后续的题解,·>这里

现在就来写下这道题的做法,其实是一个树状数组的模板题;
鉴于题目105的数据量冒泡排序的复杂度会超时,那我们间接得出冒泡排序执行次数的关键便是逆序数。
在序列中,一个数 t 前面有a个比它大的数字,那 t 的逆序数就是 a ;

对于冒泡排序的执行次数,因为每次只能往后冒一个泡,那么所有比 t 大的数字至少需要 a 次冒泡才能全部到 t 的后面;而相对于 t ,前面比 t 大的数字冒泡的优先度要更大,那最多只需要 a 次冒泡就能使所有比 t 大的数到 t 的后面;
即可以得出在 t 与它前面的大数之间只需要 a 次冒泡就能有序;而多余的冒泡操作并不会改变这些数字之间的顺序关系。那么,只需要找到序列最大逆序数便可以知道序列有序需要多少次冒泡操作了;

稍稍讲一下我自己也不是很懂的树状数组;
树状数组的二进制性质尽管十分巧妙,但是并不难理解,简单来说相当于把线段树的所有右端点都削去了;而通过剩下部分根据下标的二进制最低的非0位而决定结点深度的特殊储存方式,通过位操作即可向上更新及前n项求和;

我们可以用树状数组保存现在扫到的数字前面有多少个比它小的数字;这样我们就可以通过 已扫数字数量 减去 小于当前数字的数的数量 而求出这个数的逆序数是多少了;

不过在这之前要对数字进行离散化,使其排序完毕后单调线性递增,具体操作先标出输入数据顺序的下标,如样例的 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;
注意排序时相同数字下标越大优先度越大;

但实际上上面的离散化不是必须的。从下标有序到数值有序,之前的数值混乱度和排序后的下标混乱度应该是对偶的,那么直接对下标进行操作即可;

代码

#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))
typedef long long LL;
using namespace std;
const int maxn=100005;
struct node{
    int v,num;
}a[maxn];
bool cmp1(node a,node b){
    if(a.v!=b.v)
        return a.v<b.v;
    else
        return a.num<b.num;
}
bool cmp2(node a,node b){
    return a.num<b.num;
}
int n;
int bit[maxn+1];
int sum(int i){
    int s=0;
    while(i>0){
        s+=bit[i];
        i-=i&-i;
    }
    return s;
}
void add(int i,int x){
    while(i<=n){
        bit[i]+=x;
        i+=i&-i;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i].v);
        a[i].num=i+1;
    }
    sort(a,a+n,cmp1);
    for(int i=0;i<n;i++){
        a[i].v=i+1;
    }
    sort(a,a+n,cmp2);
    int ans=0;
    for(int j=0;j<n;j++){
        ans=max(ans,j-sum(a[j].v));
        add(a[j].v,1);
    }
    printf("%d\n",ans+1);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值