树状数组和线段树
- 树状数组相对于线段树的特点:
1、代码短
2、常数很小
-
两者关系:首先选择树状数组,树状数组解决不了再用线段树
1、树状数组
功能:
树状数组只能进行这两种操作,其他问题的解决都是转化成这两种问题
图形化:
核心公式:
lowbit(x)可以求:假设x的二进制表示后面有k个零,则返回值为2的k次方
线段树求的就是(x-lowbit(x),x]这个范围内的值!!!
lowbit函数:
两种操作代码
1、求前缀和(c是树状数组)
2、在某个位置加上一个数
注意树状数组的范围,不一定到n!!!
模板题:动态求连续区间和
#include<iostream>
using namespace std;
const int N=10010;
int a[N];//原数组
int tr[N];//树状数组
int n,m;
//lowbit函数
int lowbit(int x)
{
return x&-x;
}
//在i的位置加上v
void add(int x,int v)
{
for(int i=x;i<=n;i+=lowbit(i))
tr[i]+=v;
}
//求和
int query(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=tr[i];
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) add(i,a[i]);//在第i个位置加上a[i],初始化
while(m--)
{
int k,x,y;
scanf("%d %d %d",&k,&x,&y);
if(k==0) cout<<query(y)-query(x-1)<<endl;
if(k==1) add(x,y);
}
return 0;
}
例题:数星星
#include<iostream>
using namespace std;
const int N=32010;
int level[N],tr[N];
int n,m;
int lowbit(int x)
{
return x&-x;
}
int sum(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=tr[i];
return res;
}
void add(int x)
{
for(int i=x;i<=N;i+=lowbit(i))
tr[i]++;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
x++;
level[sum(x)]++;
add(x);
}
for(int i=0;i<n;i++)
cout<<level[i]<<endl;
return 0;
}
2、线段树
图形化表示:mid=(l+r)/2
核心函数:
两种操作:
1、单点修改
2、区间查询