洛谷 - P3374 树状数组1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 xx

  • 求出某区间每一个数的和

输入格式

第一行包含两个正整数 n,mn,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 33 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 xx 个数加上 kk

  • 2 x y 含义:输出区间 [x,y][x,y] 内每个数的和

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

输入输出样例

输入

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

输出

14
16

说明/提示

【数据范围】

对于 30\%30% 的数据,1 \le n \le 81≤n≤8,1\le m \le 101≤m≤10;
对于 70\%70% 的数据,1\le n,m \le 10^41≤n,m≤104;
对于 100\%100% 的数据,1\le n,m \le 5\times 10^51≤n,m≤5×105。

样例说明:

故输出结果14、16

思路

树状数组单点修改

这里有一个很关键的东西,叫做lowbit,lowbit是将一个二进制数的所有高位一都去掉,只留下最低位的1,比如lowbit(5)= lowbit(0101)= 0001

而如果改变x的值,就要加上自己的lowbit,一直加到n,这些节点都要加,比如一共有8个数第3个数要加上k,那么c[0011]+=k;

c[0011+0001] (c[0100])+=k;

c[0100+0100] (c[1000])+=k;

这样就能维护树状数组

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

树状数组区间查询

就是前缀和,比如查询x到y区间的和,那么就将从1到y的和 - 从1到x的和。

从1到y的和求法是,将y转为2进制,然后一直减去lowbit(y),一直到0

比如求1到7的和

ans+=c[0111];
ans+=c[0111-0001(0110)];
ans+=c[0110-0010(0100)];
ans+=c[0100-0100(c[0]无意义,结束)]
    int sum(int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=tree[x];
            x-=lowbit(x);
        }
        return ans;
    }

树状数组单点查询

从x点,一直x-=lowbit(x),沿途都加上

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

代码

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<math.h>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
int const N = 5e5 + 5;
int c[N], n;
int lowbit(int x)
{
	return x & -x;
}
int ask(int x)
{
	int res = 0;
	for (; x; x -= lowbit(x))
	{

		res += c[x];
	}
	return res;
}
void update(int x, int d)
{
	for (; x <= n; x += lowbit(x))
	{
		c[x] += d;
	}
	return;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		int t;
		cin >> t;
		update(i, t);
	}
	for (int i = 1; i <= m; ++i)
	{
		int o;
		cin >> o;
		if (o == 1)
		{
			int x, k;
			cin >> x >> k;
			update(x, k);
		}
		else
		{
			int l, r;
			cin >> l >> r;
			cout << ask(r) - ask(l - 1) << endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值