题意
题目描述
如题,已知一个数列,你需要进行下面两种操作:
-
将某一个数加上 xx
-
求出某区间每一个数的和
输入格式
第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:
-
1 x k
含义:将第 x 个数加上 k -
2 x y
含义:输出区间 [x,y]内每个数的和
输出格式
输出包含若干行整数,即为所有操作 22 的结果
思路
这题我用来作为树状数组的理解和学习吧
首先是lowbit函数 比如说 5 (101) 可以通过函数转换变成 1 (001)就是取出 第一个1这样就可以对数组进行一个二进制化的操作
int lowbit(int x)
{
return x&(-x);
}
比如说
c[1]=a[1]
c[2]=a[1]+a[2] (c[0010]=a[0001]+a[0010])
c[3]=a[3] (c[0011]=a[0011])
c[4]=c[2]+c[3]+a[4] (c[0100]=a[0100]+c[0011]+c[0010])
所以换一种看法就是 比如说 改变a[1]的值,那么将会影响到的是 c[1] c[2] c[4] c[8] c[16]
如果改变a[3]的值,那么将会影响到的是 c[3] c[4] c[8] c[16]
可以发现的是 改变a[n]的值 就会 改变 c[n] c[n+lowbit(n)] ......
所以可以写出一个单点改变的函数就是
void add(int x,int d)
{
a[x]+=d;
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=d;
}
那么再要查找呢,如果要我们找[L,R]的和呢
可以分析出要求某一个区间和,是[1,R]-[1,L]这两个区间的和相减的
然后怎么求[1,x]的区间和呢
这就要发现一个规律,结合我们第一张图
比如搜索sum(7)则 sum=c[7]+c[6]+c[4]
7 -> 6 (7-lowbit(7))
6 -> 4 (6-lowbit(6))
所以可以写出来一个求和式子
long long sum(int u)
{
int ans=0;
for(int i=u;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
题目
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
ll n,m;
ll a[maxn];
ll c[maxn];
ll lowbit(int x)
{
return x&(-x);
}
ll sum(ll u)
{
ll ans=0;
for(int i=u;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
void add(ll x,ll d)
{
a[x]+=d;
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=d;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>m;
int tmpn=n;
for(int i=1;i<=n;i++)
{
int x;cin>>x;
add(i,x);
}
while(m--)
{
int p;cin>>p;
if(p==1)
{
int x1,k;cin>>x1>>k;
add(x1,k);
}
if(p==2)
{
int l,r;cin>>l>>r;
ll ans=sum(r)-sum(l-1);
cout<<ans<<endl;
}
}
}