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

来源:互联网 发布:手机淘宝同城在哪里找 编辑:程序博客网 时间:2024/06/08 16:06

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];     //树状数组,实际大小为映射的大小Nint 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;}



原创粉丝点击