• NYOJ 116士兵杀敌(二) 树状数组


    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=116

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

    先上图,看着图解释容易理解点。

    数组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;
    }

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

     1 #include <stdio.h>
     2 #include <string.h>
     3 
     4 int tmp[1001000];
     5 int n, k;
     6 
     7 int lowbit(int N)//求n中有多少个能被2的多少次幂整除的,即2^k, 也就是树状数组的作用域
     8 {
     9     return N & (-N);
    10 }
    11 
    12 void add(int pos, int num)//添加新值到树状数组中
    13 {
    14     while(pos <= n)
    15     {
    16         tmp[pos] += num;
    17         pos += lowbit(pos);
    18     }
    19 }
    20 
    21 int Sum(int N)//求前N个数的和
    22 {
    23     int sum = 0;
    24     while(N > 0)
    25     {
    26         sum += tmp[N];
    27         N -= lowbit(N);
    28     }
    29     return sum;
    30 }
    31 
    32 int main()
    33 {
    34     int a, b, temp;
    35     char str[10];
    36     scanf("%d %d", &n, &k);
    37     for(int i = 1; i <= n; i++)
    38     {
    39         scanf("%d", &temp);
    40         add(i, temp);
    41     }
    42     for(int i = 0; i < k; i++)
    43     {
    44         scanf("%s %d %d", str, &a, &b);
    45         if(strcmp(str, "QUERY") == 0)
    46             printf("%d
    ", Sum(b) - Sum(a - 1));
    47         else
    48             add(a, b);
    49     }
    50 
    51     return 0;
    52 }
  • 相关阅读:
    webp怎么打开 webp怎么转换成jpg
    波浪运动
    缓动
    动画的封装
    单张滑动tab 组件
    明星单品tab
    多个tab选项卡
    下拉框
    购物车css样式效果
    菜单导航兼容和不兼容捕获方法
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4009223.html
Copyright © 2020-2023  润新知