树状数组题目总结(一)

来源:互联网 发布:二战中国功劳排名 知乎 编辑:程序博客网 时间:2024/05/22 09:00

树状数组首先是用来求和的, 但是这个和的具体意义不同,树状数组解决的问题就不同。

我做的第一道树状数组的题是POJ2299 Ultra--QuickSert ,这道题是求,要将一个序列非降序排列,需要做多少次交换而实际上就是求逆序数。

求逆序数是一维树状数组的一个重要应用,POJ3067 Japan也是一个求逆序数的题,还有POJ2481Cow  也是这个类型的,那我们就来总结一下,用树状数组求逆序数的方法。


树状数组求逆序数:

首先,我们按照给定的数组的顺序构造树状数组,在构造的过程中,来求没每个数的逆序数。

例如, 1 4 2 3 5 3 8 3

对于这个序列, 此时我们将树状数组初始化为memset(bit, 0, sizeof(bit)), 那么我们做另外一件事,就是记录构造树的经过,我们用数组a[ ]来表示, 那么初始化的时候就是 0 0 0 0 0 0 0 0, 也就是说数组a是一个为表示, 他表示相应位置的数字的个数, 那么第一个操作过后, a成为了1 0 0 0 0 0 0 0, 但是此时纵观数组a,比1大的位置没有其他的数, 所以你序数为0;第二个操作过后, a:1 0 0 1 0 0 0 0, 此时还是没有逆序数, 但是第三个操作后, a:1 1 0 1 0 0 0 0 , 很显然, 比2的位置大的位置, 有一个4的位置是有数字的, 那么2的逆序数为1, 这个时候,一共有3个被操作了, 而小于等于2的有两个, 也就是Sum(2), 那么逆序数的个数就是3 - 2 = 1, 以此类推。

还有一点, 那就是数组是按照原序列的顺序来安置的, 所以求出来的就是这个数在原序列的位置之前比它大的数的个数。和后面的无关。

那么POJ2299的代码, 如下:

#include <stdio.h>#include <algorithm>using namespace std;#define N 500010int n, a[N], c[N];struct node {        int v, org;}p[N];bool cmp ( node a, node b ) {        return a.v < b.v;}int lowbit ( int x ) {        return x & ( -x );}void addpoint ( int x ) {        for ( ; x <= n; x += lowbit(x) ) c[x]++;}int sum ( int x ) {        int s = 0;        for ( ; x > 0; x -= lowbit(x) ) s += c[x];        return s;}int main(){        while ( scanf("%d", &n) != EOF && n ) {                long long ans = 0;                for ( int i = 1; i <= n; ++i ) {                        scanf( "%d", &p[i].v );                        p[i].org = i;                }                sort ( p + 1, p + n + 1, cmp );                for ( int i = 1; i <= n; ++i ) a[p[i].org] = i, c[i] = 0;                for ( int i = 1; i <= n; ++i ) {                        addpoint(a[i]);                        ans += i - sum ( a[i] );                }                printf ( "%lld\n", ans );        }}

其中函数sum就是求之前有多少个小于等于这个数的数。i是当前的上传的数的个数。

原创粉丝点击