士兵杀死(两)(南阳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

士兵杀敌(一) 数组是固定的,所以能够用一个sum数组来保存每一个元素的和即可,可是不能每次都加,由于那样会超时。查询次数太多。可是这个士兵杀敌(二)就不能用那个方法来解了,由于这个是动态的,中间元素的值可能会变化。所以引出一个新的东西来。刚開始想了一下,实在是没有想到方法,就去讨论区看了看,一看好像都说用树状数组,就去找树状数组的使用方法。

先上图。看着图解释easy理解点。

数组A是原数组中的元素。数组C是树状数组中的元素,图中C数组的元素组成为A中的某些元素之和,这些元素的个数取决于它的下标能被多少个2整除,像C[1] = A[1]; C[2] = A[1] + A[2]; C[3] = A[3]; C[4] = A[1] + A[2] + A[3] + [4] = C[2] + C[3]; ……这些个数能够写一个通式C[i] = A[n - 2^k + 1] + ……+A[i]; 当中k为 i 的二进制中从右往左数的 0 的个数 ,就像6有一个, 6能够写成 2 × 3, 所以C[6] = A[5] + A[6]; 所以能够定义一个函数来求这个数.

6的二进制为0110

5的二进制为0101

6^5 = 0011

6&(6^5) = 0010 = 十进制中的2

所以函数能够这么写

int lowbit(int N)//求n中有多少个能被2的多少次幂整除的。即2^k, 也就是树状数组的作用域
{
    return N & (N ^ (N - 1));
}

也能够写成

int lowbit(int N)//求n中有多少个能被2的多少次幂整除的,即2^k, 也就是树状数组的作用域
{
    return N & (-N);
}

更改一个数的值, 就要更改次数在树状数组中的全部祖先,只是这个时间复杂度是O(logn); 以下是更改值(加入杀敌数)的函数

复制代码
void add(int pos, int num)//加入新值到树状数组中
{
    while(pos <= n)
    {
        tmp[pos] += num;
        pos += lowbit(pos);
    }
}
复制代码

以下就是求和函数。 由于这样的方法之所以快。是求他的最小树根节点的和, 最小树的个数为当前要求的n的二进制中为1的个数。即展开式中能写成不同2的幂指数的项数。

比如: 15 = 2^3 + 2^2 + 2^1 + 2^0; 所以n = 15时, 最小数有四个。求和的时间复杂度为O(logn); 

复制代码
int Sum(int N)//求前N个数的和
{
    int sum = 0;
    while(N > 0)
    {
        sum += tmp[N];
        N -= lowbit(N);
    }
    return sum;
}
复制代码

关键就是这三步, 这三步搞明确了,基本上就不成问题了。可是。当时依照 杀敌(一) 中的思维。还统计了一个总数,那样不会快,反而会慢,所以直接求即可。以下是完整的代码



#include<stdio.h>
#include<string.h>
int a[1000010];
int n,m;
int lowbit(int N)//求n中有多少个能被2的多少次幂整除的。即2^k, 也就是树状数组的作用域
{
    return N&(-N);
}
void asd(int i,int M)//加入新值到树状数组中
{
    while(i<=n)
    {
        a[i]+=M;
        i+=lowbit(i);
    }
}
int sum(int N)//求前N个数的和
{
    int sum=0;
    while(N>0)
    {
        sum+=a[N];
        N-=lowbit(N);
    }
    return sum;
}
int main()
{
    int i,t,a,b;
    char str[10];
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&t);
        asd(i,t);
    }
    while(m--)
    {
        scanf("%s %d %d",str,&a,&b);
        if(!strcmp(str,"QUERY"))
        printf("%d
",sum(b)-sum(a-1));
        else
        asd(a,b);
    }
    return 0;
}
    
    


版权声明:本文博主原创文章,博客,未经同意不得转载。

原文地址:https://www.cnblogs.com/zfyouxi/p/4828172.html