二叉索引树
树状数组
又名二叉索引树,是一种与线段树相似的数据结构
他们能使对一个区间的数修改以及查询的速度提升许多
树状数组模板1
#include<iostream>
#include<cstdio>
using namespace std;
int tree[2333333];
int sum[233333];
int a[2333333];
int n,m;
#define lowbit(x) (x&(-x))
inline void init()
{
for(int x,i=1;i<=n;i++)
{
cin>>x;
sum[i]=sum[i-1]+x;
tree[i]=sum[i]-sum[i-lowbit(i)];
}
}
inline void add(int a,int val)
{
while(a<=n)
{
tree[a]+=val;
a+=lowbit(a);
}
}
inline int query(int a)
{
int res=0;
while(a)
{
res+=tree[a];
a-=lowbit(a);
}
return res;
}
int main()
{
ios_base::sync_with_stdio(false);
cout.tie(NULL);
cin.tie(NULL);
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
if(x == 1)
{
add(y,z);
}
else{
cout<<query(z)-query(y-1)<<'\n';
}
}
}
中间有一个小小的卡常
inline void init()
{
for(int x,i=1;i<=n;i++)
{
cin>>x;
sum[i]=sum[i-1]+x;
tree[i]=sum[i]-sum[i-lowbit(i)];
}
}
该函数能将初始化树状数组的复杂度优化从O(nlogn)->O(n)
树状数组模板2
#include<iostream>
#include<cstdio>
using namespace std;
int tree[2333333];
int c[2333333];
int n,m;
inline int lowbit(int x) {return x&-x;}
inline void add(int a,int val)
{
while(a<=n)
{
tree[a]+=val;
a+=lowbit(a);
}
}
inline int query(int a)
{
int res=0;
while(a)
{
res+=tree[a];
a-=lowbit(a);
}
return res;
}
int main()
{
ios_base::sync_with_stdio(false);
cout.tie(NULL);
cin.tie(NULL);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>c[i];
}
for(int i=1;i<=m;i++)
{
int l,x,y,z;
cin>>l;
if(l == 1)
{
cin>>x>>y>>z;
add(x,z);
add(y+1,-z);
}
else
{
cin>>x;
int U=query(x)+c[x];
cout<<U<<'\n';
}
}
}
- 对序列进行区间修改
- 我们使用了另一个树状数组来辅助修改
- 记录左右端点的修改
- 当我们访问b的前缀和时
- 就相当于访问之前操作对[1~x]的影响
线段树模板1
#include<iostream>
#include<cstdio>
using namespace std;
const long long maxn=1e5+5;
long long n,m;
long long sum[maxn];
long long tree[2][maxn];
inline long long lowbit(long long x) {return x&-x;}
inline void add(long long k,long long a,long long val)
{
while(a<=n)
{
tree[k][a]+=val;
a+=lowbit(a);
}
}
inline long long query(long long k,long long a)
{
long long res=0;
while(a)
{
res+=tree[k][a];
a-=lowbit(a);
}
return res;
}
int main()
{
cin>>n>>m;
long long x;
for(long long i=1;i<=n;i++)
{
cin>>x;
sum[i]=sum[i-1]+x;
}
for(long long i=1,x,y,z,g;i<=m;i++)
{
cin>>x>>y>>z;
if(x == 1)
{
cin>>g;
add(1,y,g);
add(1,z+1,-g);
add(0,y,y*g);
add(0,z+1,-(z+1)*g);
}
else{
cout<<(sum[z]+(z+1)*query(1,z)-query(0,z))-(sum[y-1]+y*query(1,y-1)-query(0,y-1))<<'\n';
}
}
}
对单点查询=访问原值+b树状数组前缀和
所以区间查询相当于对多个点进行单点查询
那么a前缀和整体增加值为
\(\sum_{i=1}^x{\sum_{j=1}^i{b[j]}}\)
\(\sum_{i=1}^x{\sum_{j=1}^i{b[j]}} = \sum_{i=1}^x{(x-i+1)*b[i]} = (x+1)\sum_{i=1}^xb[i] - \sum_{i=1}^x{i*b[i]}\)
所以我们增加一个树状数组,维护 \(i*b[i]\)的前缀和\(\sum_{i=1}^x{i*b[i]}\)
另外我们维护原数组前缀和sum,对于每条 Q l r
可得出
访问原值前缀和与上述两个树状数组的值在套用上式与另一端点作差即可得出答案