树状数组的应用
1、单点更新,区间求值。(HDU1166)
2、区间更新,单点求值。(HDU1556)
3、二维树状数组 (poj1195)
4、求逆序对。(HDU2838)
5、求逆序数。(HDU2689)
对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个
不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序对。一个排列中所有逆序对总数叫做这个排列的逆序数。
例如:标准为次序为1
2 3 4,则4 3 2 1,的逆序数为6,逆序对为(4,3) (4,2) (4,1) (3,2) (3,1) (2,1);
树状数组如下图所示:
c1=a1 c3=a3 c5=a5 c7=a7 c9=a9
c2=a1+a2 c4=a1+a2+a3+a4 c6=a5+a6 c8=a1+a2+a3+a4+a5+a6+a7+a8 c10=a9+a10
c11=a11........c16=a1+a2+a3+a4+a5+.......+a16
分析上面的几组式子可知,当 i 为奇数时,ci=ai ;当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,6 的因子中有 2 的一次幂,等于 2 ,所以 c6=a5+a6(由六向前数两个数的和),4 的因子中有 2 的两次幂,等于 4 ,所以 c4=a1+a2+a3+a4(由四向前数四个数的和)。
这段代码理解为树状数组向前,或者向后衍生用的
int lowbit(int n) { return n&(-n); }
这段代码用来更新树状数组用的
void Add(int n, int v) { while (n>0) { Tree[n] += v; n -= lowbit(n); } }
这段代码用来求树状数组前缀和的
int get(int n) { int sum = 0; while (n<=num) { sum += Tree[n]; n += lowbit(n); } return sum; }
hdu1556
#include <iostream> using namespace std; #define MAX 100001 int Tree[MAX]; int a, b, num; int lowbit(int n) { return n&(-n); } void Add(int n, int v) { while (n>0) { Tree[n] += v; n -= lowbit(n); } } int get(int n) { int sum = 0; while (n<=num) { sum += Tree[n]; n += lowbit(n); } return sum; } int main() { int i; while (scanf("%d", &num)!=EOF && num) { memset(Tree, 0, sizeof(Tree)); for (i=1; i<=num; i++) { scanf("%d%d",&a, &b); Add(b, 1); Add(a-1, -1); } for (i=1; i<num; i++) printf("%d ", get(i)); printf("%d\n", get(num)); } return 0; }