zoukankan      html  css  js  c++  java
  • 树状数组学习笔记

    树状数组学习笔记

    P3374 【模板】树状数组 1

    //树状数组1
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int n, m, t, tree[500007];
    
    int lowbit(int k)
    {
    	return k & -k;
    }
    
    void add(int b, int c)
    {
    	while(b <= n)
    	{
    		tree[b] += c;
    		b += lowbit(b);
    	}
    }
    
    int sum(int b)
    {
    	int ans = 0;
    	while(b != 0)
    	{
    		ans += tree[b];
    		b -= lowbit(b);
    	}
    	return ans;
    }
    
    int main() {
    	int a, b, c;
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i++)
    	{
    		scanf("%d", &t);
    		add(i, t);
    	}
    	for(int i = 1; i <= m; i++)
    	{
    		scanf("%d%d%d", &a, &b, &c);
    		if(a == 1)
    			add(b, c);
    		if(a == 2)
    			printf("%d
    ", sum(c) - sum(b - 1));	
    	}
    	return 0;
    } 
    

    结构

    img

    c就是树状数组

    单点插入

    他们之间的关系如下

    [c[1] = a[1] ]

    [c[2] = a[1]+a[2] ]

    [c[3] = a[3] ]

    [c[4] = a[1] + a[2] + a[3] + a[4] ]

    好像看不出什么规律

    用二进制表示

    [c[0001] = a[0001] ]

    [c[0010] = a[0010] + a[0001] ]

    [c[0011] = a[0011] ]

    [c[0100] = a[0100] + a[0010] + a[0011] + a[0001] ]

    我们引入lowbit函数

    这个函数可以帮我们求出一个数的二进制表示的最后位的1

    lowbit求法

    int lowbit(int k)
    {
    	return k & -k;
    }
    

    例子

    ​ $$5 = 0101, 1 = 0001$$

    ​ $$lowbit(5) = 1$$

    void add(int b, int c)
    {
    	while(b <= n)
    	{
    		tree[b] += c;
    		b += lowbit(b);
    	}
    }
    

    当我在插入一个数或改变一个数的时候

    这个数会影响它后面的每个c数组

    所以每次给b加上lowbit(b)来模拟这个数后面的每一个c数组下标

    区间求和

    int sum(int b)
    {
    	int ans = 0;
    	while(b != 0)
    	{
    		ans += tree[b];
    		b -= lowbit(b);
    	}
    	return ans;
    }
    

    由于c数组是类似于前缀和的东西

    所以我们如果求区间a到b的和

    输出sum(b) - sum(a - 1)即可


    P3368 【模板】树状数组 2

    //树状数组2
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    int n, m, op, x, y, z;
    int a[500007], p[500007];
    
    int lowbit(int x)
    {
    	return x & -x;
    }
    
    void add(int x, int y)
    {
    	while(x <= n)
    	{
    		p[x] += y;
    		x += lowbit(x);
    	}
    }
    
    int chaf(int x)
    {
    	int ans = 0;
    	while(x != 0)
    	{
    		ans += p[x];
    		x -= lowbit(x);
    	}
    	return ans;
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i++)
    		scanf("%d", &a[i]);
    	for(int i = 1; i <= m; i++)
    	{
    		scanf("%d", &op);
    		if(op == 1)
    		{
    			scanf("%d%d%d", &x, &y, &z);
    			add(x, z);
    			add(y + 1, -z);
    		}
    		else if(op == 2)
    		{
    			scanf("%d", &x);
    			printf("%d
    ", a[x] + chaf(x));
    		}
    	}
    	return 0;
    }
    

    区间修改

    区间修改一般使用差分实现

    具体一点是用树状数组维护差分数组

    即x到y加z时

    add(x, z);
    add(y + 1, -z);
    

    单点查询

    看代码


    ​ 2019年8月19日还差练习题

  • 相关阅读:
    第31课
    第30课
    第29课
    第28课
    第27课
    ubuntu 允许root用户登录到ssh
    openfire 安装配置时出现The Openfire database schema does not appear to be installed. Follow the installati错误的解决方案
    ubuntu 16 “无法获得锁”解决方案
    增加phpmyadmin导入文件上限
    ubuntu-查看所有用户
  • 原文地址:https://www.cnblogs.com/wyswyz/p/11377244.html
Copyright © 2011-2022 走看看