简单基础树状数组求逆序数 POJ2299

本文解析了POJ 2299题目的解题思路,介绍了如何利用树状数组求解数组的逆序数,进而得出冒泡排序所需的最小交换次数。文章还分享了使用映射技巧解决大数据范围问题的经验。

POJ :http://poj.org/problem?id=2299

题意:给一个大小为n的数组,乱序!  对数组进行排序,排序的办法是,每次只能交换相邻的两个数字。(也就是冒泡排序)

询问,最少的冒泡排序的交换次数为多少

n大小为500000,每个数字的大小为999999999(9个9,int范围内)。

很惭愧,这么简单的题目,一开始我没有什么清晰的思路。。。太蠢了。

后来明白是要求这个数组的“逆序数”,即为答案。

逆序数:所有在数组中的数,分别求 其在数组中,它后面比它小的数字的个数  加起来。

比如样例 9 1 0 5 4 ,那么9后面有4个比它小的,1有一个,0没有,5有1个,4没有。所以逆序数就是 4+1+1=6,即为答案,而这也是9要冒泡交换的次数。

以上都是线代的内容,惭愧,线代一直睡觉,擦线过的。。。。

用树状数组如何求逆序数?

其实就是求,对于数组里每一个数,在后面的  比它小的 数字的个数。

这也是树状数组的一个很常用的功能。   将数的大小开一个数组,从数组的一边,边扫,边更新树状数组,这时树状数组记录的是“从数组前到这个位置,各个数出现的个数”,同时边进行查询操作。

然而,数的大小此题为999999999,这么大的数组是开不起来的,但是n最大为500000,所以很自然就想到了,对题目给的数字进行从1到n的一个映射(离散化),所以想到映射就能写出此题了。

 

映射的步骤很偷懒的就想到了用map。今日也是涨了姿势,都说map慢,一直没有见证。理论复杂度nlogn的map,交上去以后TE了。怀疑人生,以为是哪个地方写错了,多交了几发,都是TE。。。

于是,改用普通排序,自己手动映射,就过了。。。

以后能不用map就不用map吧。。

还有POJ是不咨瓷 <bits/stdc++>的。。

上一下代码。。

 

#include "stdio.h"
#include "map"
#include "cstring"
#include "algorithm"
using namespace std;
#define inf 500009
#define INF 999999999
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)
int n;
int c[inf];     //树状数组,实际大小为映射的大小N
int m[inf];//映射原始下标到排序后下标
ll ans;
struct node
{
    int x,id;
    bool operator< (const node& j)const
    {
        return x<j.x;
    }
}num[inf];      //题数据以及下标
void init()
{
    memset(c,0,sizeof c);
    ans=0;

    sort(num+1,num+n+1);
    int i;
    loop(i,1,n+1)
        m[num[i].id]=i;
}
int lowbit(int i)
{
    return i&-i;
}
void add(int i)
{
    while(i<=n)
    {
        c[i]++;
        i+=lowbit(i);
    }
}
ll query(int i)
{
    ll sum=0;
    while(i>0)
    {
        sum+=(ll)c[i];
        i-=lowbit(i);
    }
    return sum;
}

int main()
{
    int i,j;
    while(scanf("%d",&n)&&n)
    {
        loop(i,1,n+1){scanf("%d",&num[i].x);num[i].id=i;}
        init();
        for(i=n;i>0;i--)
        {
            ans+=query(m[i]-1);
            add(m[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值