树状数组与分治法求逆序数

树状数组:

可以把数一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 j- sum( a[j] ),其中 j 为当前已经插入的数的个数, sum( a[j] )为比 a[j] 小的数的个数,j- sum( a[j] ) 即比 a[j] 大的个数, 即逆序的个数。最后需要把所有逆序数求和,就是在插入的过程中边插入边求和。

例如

比如输入一个9 1 0 5 4的数组,那么BIT[j]树状数组的建立是在,

    下标 0 1 2 3 4 5 6 7 8 9

    数组 1 1 0 0 1 1 0 0 0 1

一开始的BIT是空的,每计算完一个位之后就把此位加一add(a[j], 1),所以当计算到第a[j]位时,前面的a[i]...a[0]值是1的代表存在i < j && a[i] < a[j]的数.

归并:

开始把长度为n的数组a分成两个长度为n/2数组的数组b和c

因为i < j && a[i] > a[j] 的逆序数存在只有三种情况:

1、i和j 都在数组b中

2、i和j都在数组c中

3、i 在数组b 中j 在数组c中

把三种情况的逆序数加起来即可

/********************
 * Author:fisty
 * Data:2014-12-6
 * *****************/

//求逆序数
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAX_N 1000
typedef long long ll;
//输入
vector<int > A;
//merge
ll merge_count(vector<int> &a){
        int n = a.size();
        if(n <= 1) return 0;

        ll cnt = 0;
        //把数列a分成b和c进行分治
        vector<int> b(a.begin(), a.begin()+n/2);
        vector<int> c(a.begin()+n/2 , a.end());
        
        
        cnt += merge_count(b);   //(i,j) 在 b时
        cnt += merge_count(c);   //(i, j)在 c时 
        /*cout<< "b: ";
        for(int i = 0;i < b.size(); i++)
                printf("%d ", b[i]);
        printf("      ");
        cout << "c: ";
        for(int i = 0;i < c.size(); i++)
                printf("%d ", c[i]);
        printf("\n");
        */
        //当(i,j)横跨b, c数列时
        int ai = 0, bi = 0, ci = 0;
        while(ai < n){
                if(bi < b.size() && (ci == c.size() || b[bi] <= c[ci])){        
                        a[ai++] = b[bi++];
                }else{
                        cnt += n/2 - bi;
                        //printf("nixu:%d \n", n/2 - bi);
                        a[ai++] = c[ci++];
                }
        }
        cout << "a: ";
        /*for(int i = 0;i < a.size(); i++)
                printf("%d ", a[i]);
        printf("\n");
        */
        return cnt;
}
void solve1(){
        printf("%lld\n", merge_count(A));
}
//bit
int bit[MAX_N];
int sum(int i){
        int sum = 0;
        while(i > 0){
                sum += bit[i];
                i -= i&(-i);
        }
        return sum;
}
void add(int i ,int x){
        while(i <= MAX_N){
                bit[i] += x;
                i += i&(-i);
        }
}


void solve2(){
        memset(bit, 0, sizeof(bit));
        ll ans = 0;
        for(int i = 0;i < A.size(); i++){
                ans += i - sum(A[i]);
                add(A[i], 1);
        }
        printf("%lld\n", ans);
}
int main(){
        int n;  
        scanf("%d", &n);
        for(int i = 0;i < n; i++){
                int t;
                scanf("%d", &t);
                A.push_back(t);
        }
        //分治法求解
        solve1();  
        //树状数组求解
        solve2();

        return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值