树状数组

来源:互联网 发布:家庭理财记账软件 编辑:程序博客网 时间:2024/05/31 18:54


士兵杀敌(二)(南理116)

时间限制:1000 ms  |  内存限制:65535 KB

难度:5

描述

南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的。

小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧。

南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新增的杀敌数。

输入

只有一组测试数据
第一行是两个整数N,M,其中N表示士兵的个数(1<N<1000000),M表示指令的条数。(1<M<100000)
随后的一行是N个整数,ai表示第i号士兵杀敌数目。(0<=ai<=100)
随后的M行每行是一条指令,这条指令包含了一个字符串和两个整数,首先是一个字符串,如果是字符串QUERY则表示南将军进行了查询操作,后面的两个整数m,n,表示查询的起始与终止士兵编号;如果是字符串ADD则后面跟的两个整数I,A(1<=I<=N,1<=A<=100),表示第I个士兵新增杀敌数为A.

输出

对于每次查询,输出一个整数R表示第m号士兵到第n号士兵的总杀敌数,每组输出占一行

样例输入

5 6

1 2 3 4 5

QUERY 1 3

ADD 1 2

QUERY 1 3

ADD 2 3

QUERY 1 2

QUERY 1 5

样例输出

6

8

8

20

 

 

 

#include<stdio.h>

 

int tree[1000005];//tree为树状数组

 

void swap(int *a, int *b) {

  int *temp = a;

  a = b;

  b = temp;

}

 

int lowBit(int x) {

  return x & (-x); //访问树状数组

}

 

void modify(int pos, int num, int N) {

  while (pos <= N) {//1开始访问树状数组并增加值

    tree[pos] += num;

    pos += lowBit(pos);

  }//traverse遍历

}

 

int getSum(int pos) {

  int sum = 0;

  while (pos > 0) {//1pos的和

    sum += tree[pos];

    pos -= lowBit(pos);

  }

  return sum;

}

 

int main() {

  int N, M, kill;

  scanf("%d%d", &N, &M);

  for (int i = 1; i <= N; i++) {

    scanf("%d", &kill);

    modify(i, kill, N);

  }

  int m, n;

  char order[10];

  while (M--) {

    scanf("%s", order);

    if (order[0] == 'Q') {

      scanf("%d%d", &m, &n);

      if (m > n) swap(&m, &n);

      printf("%d\n", getSum(n)-getSum(m-1));       

    }else {

      scanf("%d%d", &m, &n);

      modify(m, n, N);

    }

  }

  return 0;

}

 

 

1.图一


1.图二


(图片网上找的)

1.树状数组C从与普通数组A的关系:

C[1] = A[1];

C[2] = C[1] + A[2] = A[1] + A[2];

C[3] = A[3];

C[4] = C[2] + C[3] + A[4] = C[1] + A[2] + A[3] + A[4] = A[1] + A[2] + A[3] + A[4];

C[5] = A[5];

C[6] = C[5] + A[6] = A[5] + A[6];

C[7] = A[7];

C[8] = C[4] + C[6] + C[7] + A[8] = C[2] + C[3] + A[4] + C[5] + A[6] + A[7] + A[8] = C[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8] = A[1] + A[2] + A[3] + A[4] + A[5] +A[6] + A[7] + A[8];


 

2.那上述关系是怎么确定呢:

C[i] = A[n - 2^k + 1] + ...... + A[i];//n表示数组长度,k表示i的二进制时倒数第一个1后面的0的个数。如:10100倒数第一个1后面0的个数为2.

i为奇数时C[i] = A[i];


3.下面通过研究代码进一步理解怎么实现。

代码一:

int lowBit(int x) {

  return x & (-x); //求的是2^k;

}

x表示C数状数组的下标。

 

代码二:

void modify(int pos, int num, int N) {//N表示数组长度,pos表示此时树状数组的下标。num表示放入树状数组的值。

初始时树状数组的值都为0

  while (pos <= N) {//pos开始访问结点的父节点,并增加值

    C[pos] += num;

    pos += lowBit(pos);

  }//traverse遍历

}

假设pos = 1

此时C[1] += num;

由图二可知C[1]的父节点为C[2],C[2] = C[pos += lowBit(pos)] = C[1 + lowBit(1)];

pos = 2;

此时C[2] += num;

由图二可知C[2]的父节点为C[4],C[4] = C[pos += lowBit(pos)] = C[2 + lowBit(2)];

pos = 4;

此时C[4] += num;

.........

......

代码三:

int getSum(int pos) {//1pos的和

  int sum = 0;

  while (pos > 0) {

    sum += C[pos];

    pos -= lowBit(pos);

  }

  return sum;

}

假设pos = 12;

此时sum += C[12]

由图二可知pos减去lowBit(pos) = 4;pos = 8;

pos = 8;

此时sum += C[8];

由图二可知pos减去lowBit(pos) = 8;pos = 0;

所以此时跳出循环。


原创粉丝点击