lowbit(x)=x&(-x)
lowbit(x)可以理解为能整除x的最大2的幂次
存放的是i号位之前(含i号位,下同)lowbit(i)个整数之和
C[i]的覆盖长度是lowbit(i)[也可理解为管辖范围]
将C[i]画成二维图容易理解
树状数组的下标必须从1开始
C[x]=A[x-lowbit(x)+1]+···+A[x]
C[x]=A[x-lowbit(x)+1]+···+A[x]可推得
SUM(1,x)=SUM(1,x-lowbit(x))+C[x]
int getSum(int x) {//返回前x个整数的和
int sum = 0;//用于记录和
for (int i = x; i > 0; i -= lowbit(i)) {
sum+=c[i];
}
return sum;
}
**技巧:**如果要求数组下标在区间[x,y]内的数之和,可以转换为getSum(y)-getSum(x-1)解决
寻找树状数组C中能覆盖A[x]的元素,让它们都加上v
找到距离当前C[x]的最近的能覆盖C[x]的C[y]:lowbit(y)必须大于lowbit(x):
找一个尽可能小的整数a使得lowbit(x+a)>lowbit(x) ,最小的a为lowbit(x)
void update(int x,int v){
for(int i=x;i<=MAXV;i+=lowbit(i)){//注意i必须能取到N
c[i]+=v;//让c[i]加上v,然后c[i+lowbit(i)]加上v
}
}
思路1
hash数组:hash[x]记录整数x当前出现的次数,从左到右依次遍历序列A,假设当前访问的是A[i]则令hash[A[i]]++,则在序列中在A[i]左边比A[i]小的数个数等于hash[1]+hash[2]+···+hash[A[i]-1]
思路2
使用update(A[i],1)和getSum(A[i]-1)解决
#include
#include
const int maxn = 10010;
#define lowbit(i) ((i)&(-i)) //lowbit写成宏定义的形式,注意括号
int c[maxn];//树状数组
//update函数将第x个整数加上v
void update(int x,int v){
for(int i=x;i<maxn;i+=lowbit(i)){//i
c[i]+=v;//让c[i]加上v,然后让c[i+lowbit(i)]加上v
}
}
//getSum返回前x个整数之和
int getSum(int x){
int sum=0;//记录和
for(int i=x;i>0;i-=lowbit(i)){//注意是i>0:树状数组的下标从1开始
sum+=c[i];//累计c[i],把问题缩小为SUM(1,i-lowbit(i))
}
return sum;//返回和
}
int main(){
int n,x;
scanf("%d",&n);
memset(c,0,sizeof(c));//树状数组的初值为0
for(int i=0;i<n;i++){
scanf("%d",&x);//输入序列元素
update(x,1);//x的出现次数加一
printf("%d\n",getSum(x-1));//查询当前小于x的数的个数
}
return 0;
}
边界问题的考虑
当A[i]<=N不成立时
把A[i]与1~N对应起来
解决方法:离散化:设置一个临时的结构体数组,存放输入的序列元素的值以及原始序号,在输入完毕后数组按val从小到大排序,排序完后按照“计算排名”的方式将“排名”根据原始序号pos存入一个新数组中。离散化适用于离线查询
对于在线查询,可以先把所有操作记录下来,然后对其中出现的数据进行离散化,之后按记录下来的操作顺序正常进行“在线查询”
#include
#include
#include
using namespace std;
const int maxn = 100010;
#define lowbit(i) ((i)&(-i)) //lowbit写成宏定义的形式,注意括号
struct Node {
int val;//序列元素的值
int pos;//原始序号
} temp[maxn]; //temp数组临时存放输入数据
int A[maxn];//离散化后的原始数组
int c[maxn];//树状数组
//update函数将第x个整数加上v
void update(int x, int v) {
for (int i = x; i < maxn; i += lowbit(i)) {//i
c[i] += v;//让c[i]加上v,然后让c[i+lowbit(i)]加上v
}
}
//getSum函数返回前x个整数的和
int getSum(int x) {
int sum = 0;//记录和
for (int i = x; i > 0; i -= lowbit(i)) {//注意是i不是i>=0
sum += c[i];//累计c[i],把问题缩小为SUM(1,i-lowbit(i))
}
return sum;//返回和
}
//按val从小到大排序
bool cmp(Node a, Node b) {
return a.val < b.val;
}
int main() {
int n;
scanf("%d", &n);
memset(c, 0, sizeof(c));//树状数组初始值为0
for (int i = 0; i < n; i++) {
scanf("%d", &temp[i].val);//输入序列元素
temp[i].pos = i;//原始序号
}
//离散化
sort(temp, temp + n, cmp);//按val从小到大排序
for (int i = 0; i < n; i++) {
//与上一个元素值不同时,赋值为元素隔宿
if (i == 0 || temp[i].val != temp[i - 1].val) {
A[temp[i].pos] = i + 1;//必须从1开始!树状数组的性质
} else {//与上一个元素值相同时可以直接继承
A[temp[i].pos] = A[temp[i - 1].pos];
}
}
//正式进入更新和求和的操作
for (int i = 0; i < n; i++) {
update(A[i], 1);//A[i]的出现次数加1
printf("%d\n", getSum(A[i] - 1));//查询当前小于A[i]的数的个数
}
return 0;
}
//求序列元素第K大
int findKthElement(int K){
int l=1,r=maxn,mid;//初始区间为[1,maxn]
while(l<r){//循环,直到[l,r]能锁定单一元素
mid=(l+r)/2;
if(getSum(mid)>=K) r=mid;//所求位置不超过mid
else l=mid+1;//所求位置大于mid
}
return l;//返回二分夹出的元素
}
求A[a] [b]~A[x] [y] 这个子矩阵的元素之和:计算getSum(x,y)-getSum(x-1,y)-getSum(x,y-1)+getSum(x-1,y-1)
对于高维只需要把for循环改为相应的重数
int c[maxn][maxn];
//二维update函数位置(x,y)的整数加上v
void update(int x,int y,int v){
for(int i=x;i<maxn;i+=lowbit(i)){
for(int j=y;j<maxn;j+=lowbit(j)){
c[i][j]+=v;
}
}
}
//二维getSum函数返回(1,1)到(x,y)的子矩阵中元素之和
int getSum(int x,int y){
int sum=0;
for(int i=x;i>0;i-=lowbit(i)){
for(int j=y;j>0;j-=lowbit(j)){
sum+=c[i][j];
}
}
return sum;
}