树状数组求逆序对

一般来说,求逆序对,方法如下:
1、如果 N<10000,可以用冒泡排序,交换次数就是逆序对数量,时间复杂度 O(n2),空间复杂度 O(n)
2、如果 N>=10000,用归并排序,是分治思想,时间复杂度 O(nlog2n),空间复杂度 O(2*n),见上面参考代码。
3、如果 K 表示最大元素和最小元素的差值,当 N 大于400000,K=50000 的时候,树状数组比分治更快,时间复杂度 O(nlog2K),空间复杂度 O(K)。

树状数组求逆序对的方法,在考场上不失一个好的选择,因为它不仅速度更快,而且代码也比较简单,因为我们是以数的大小为下标,所以情况可以分为两种,
第一种为题目给的需要求逆序对的数列的最大值太大,如果直接作为下标会浪费大量的空间,这时候需要离散化点值,使它缩小到我们可控的范围之内;
第二种则是数列大小恰好在N的范围内,即a[]max<=N,可以直接计算;

我们巧妙地运用了巧妙的树状数组,其中树状数组里面的a[](原数组)的下标表示的就是A序列里面的元素。例如:当A={3,3,1,9,11},循环时依次在a[3],a[3],a[1],a[9],a[11]加一。聪明的读者应该知道这个方法怎样实现了。没错,就是先读入元素ai,再将a[i]加一,然后用树状数组的特点在O(log2K)的时间复杂度求出Σa[k](1<=k<=i)。这样对于之前读入而且小于等于ai的元素个数为Σa[k],读入的ai为第i个元素,所以在ai前面且比ai大的元素为i-Σa[k],问题得以解决。

我们来看一道例题:

Prince对他在这片大陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求助于你,帮助他完成这个梦想。 一串数列即表示一个世界的状态。
平衡是指这串数列以升序排列,而从一串无序数列到有序数列需要通过交换数列中的元素来实现。KB的能量只能交换相邻两个数字。他想知道他最少需要交换几次就能使数列有序。
输入格式
第一行为数列中数的个数 N(n≤100000)。
第二行为 N 个数 a1~an (每个数小于100000),表示当前数列的状态。
输出格式
输出一个整数,表示最少需要交换几次能达到平衡状态。
这道题的题意就是求数列a中的逆序对的个数,我们注意到,“a1~an (每个数小于100000)”,所以这道题也就不需要离散化,可以直接求

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cstring>
#include<string>
#include<cctype>
#include<set>
#include<queue>
using namespace std;
long long  n,x,tree[100005],ans; //不用long long 会在最后一个点溢出 
//---------------------
inline int lowbite(int i)
{
    return i & (-i);
}
//---------------------
inline void Update(int i,long long t)
{
    while(i<=n)//体会一下为什么我们用a[]做下标,却是i<=N
    {
       tree[i]+=t;
       i+=lowbite(i);
    }
}
//---------------------
inline int query(long long i)
{
    long long  tmp=0;
    while(i>0)
    {
        tmp+=tree[i];
        i-=lowbite(i);
    }
    return tmp;
}
//---------------------
int main()
{
    freopen("dream.in","r",stdin);

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        Update(x,1);
        ans+=(i-query(x));
    }
    cout<<ans;
    return 0;
}

代码中的问题就是因为“a1~an (每个数小于100000)”,所以就可以直接i<=n,否则就需要离散化。

POJ2299就是需要离散化得情况,离散化得过程如下:

 离散化是一种常用的技巧,有时数据范围太大,可以用来放缩到我们能处理的范围,必要的是建立一个结构体a[n],v表示输入的值,order表示原i值,再用一个数组aa[n]存储离散化后的值  
 例如: 
 i:1 2 3 4 5 
 v: 9 0 1 5 4 
 排序后:0 1 4 5 9 
 order:2 3 5 4 1 如果建立映射:aa[a[i].order]=i; 
 aa:5 1 2 4 3 
 即原本的9经过排序应该在第5位,现在aa[1]=5,对应原来的9,大小次序不变,只是将9缩小到了5

其余过程大体同上,在此就不赘述。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值