若是想学习 数组树状区间操作+区间和 模板题的同学可以直接点击链接
树状数组,是一个用于在近似 O(logn)时间内动态修改以及查询前缀和的数据结构
我们以16为例子,把抽象的二进制过程画图具体化
函数lowbit()
我们可以观察到每一个数的下标加上它的二进制最低位等于上一层的下标,我们可以知道x&-x可以取到它的二进制最低位,
函数add()
运用配合函数lowbit()、运用前缀和
函数sum()
求1-i的和,对数组tree[i]逆向减lowbit()求和
该模板有区间修改和区间求和
#include<bits/stdc++.h>
using namespace std;
const int MAX=50005;
int a[MAX],tree[MAX],n;
int lowbit(int x) //找最低位的1
{
return x&-x;
}
void add(int i,int x)//修改数据在i加x
{
while(i<=n)
{
tree[i]+=x;
i+=lowbit(i);
}
}
int sum(int i)
{
int s=0;
while(i>0)
{
s+=tree[i];
i-=lowbit(i);
}
return s;
}
void range_add(int l, int r, int a) //区间修改
{
add(l, a);
add(r + 1, -a);
}
ll range_sum(int l, int r) //区间求和
{
return sum(r) - sum(l - 1);
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
}
例题
树状数组入门应用求 逆序对 (求数组右边比它小的个数和)
每次加入一个数x,判断x~MAX中存在多少个数,也就是在加入x前判断前面出现多少次比x大的数的个数。
力扣 315. 计算右侧小于当前元素的个数
codeforces 180. Inversions
牛客 逆序数
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
ll tree[100001],n,m;
ll lowbit(ll x)
{
return x&-x;
}
void add(ll i,ll x)
{
while(i<=100000)
{
tree[i]+=x;
i+=lowbit(i);
}
}
ll sum(ll i)
{
ll res=0;
while(i>0)
{
res+=tree[i];
i-=lowbit(i);
}
return res;
}
ll range_sum(ll l,ll r)
{
return sum(r)-sum(l-1);
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);
cin>>n;
ll ans=0;
for(int i=1;i<=n;i++)
{
cin>>m;
ans+=range_sum(m,100000);
add(m,1);
}
cout<<ans;
return 0;
}