树状数组题目总结(一)
2012-10-07 22:19
225 查看
树状数组首先是用来求和的, 但是这个和的具体意义不同,树状数组解决的问题就不同。
我做的第一道树状数组的题是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的代码, 如下:
其中函数sum就是求之前有多少个小于等于这个数的数。i是当前的上传的数的个数。
我做的第一道树状数组的题是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 500010 int n, a , c ; struct node { int v, org; }p ; 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是当前的上传的数的个数。
相关文章推荐
- 线段树和树状数组题目总结(未完)
- ( poj 2352,poj 3067, poj2481)树状数组题目总结(二)
- 线段树和树状数组题目总结(未完)
- 树状数组学习以及题目总结
- 树状数组题目总结三( 上)( HOJ 2275 Number sequence )
- 程序员面试题目总结--数组(一)【递归求数组所有元素和、用一个for循环打印出一个二维数组、用递归判断数组是否是递增、有序数组中删除重复元素】
- 树状数组总结
- 树状数组总结
- 树状数组(总结篇)
- poj 2155 Matrix(二维树状数组,好题)中等难度题目,更新区域,查询单点
- 树状数组题目---HDU 1541 stars 及其变形(降维思想)
- 树状数组总结二
- 南阳理工acm 题目116 士兵杀敌(二)树状数组
- 树状数组总结
- 树状数组题目
- HDOJ 题目4000 Fruit Ninja(树状数组)
- 树状数组专题总结1
- 程序员面试题目总结--数组(三)【旋转数组的最小数字、旋转数组中查找指定数、两个排序数组所有元素中间值、数组中重复次数最多的数、数组中出现次数超过一半的数】
- HDOJ题目1541 Stars(树状数组单点更新)
- HDOJ 题目1166 敌兵布阵(树状数组单点跟新)