树状数组 ——神奇而小巧的数据结构
树状数组同样可以在O(logN)的时间复杂度下完美的解决引例中的所有操作,同样我们只需要一个一维数组tree[]。其中tree[i]表示[i-(i&(-i))+1,i]这个区间的 和(戒者最大值,以下只讨论和)。其 中i&(-i)表示i的二进制表示中最末一个1 的权值。例如tree[12]表示的是 [1001,1100]这一段的和
若sum(i)表示[1,i]这个区间的和。则, sum(15)=tree[15]+tree[14]+tree[12]+ tree[8]。注意:树状数组的下标必须从1开始!
Step 1:初始化。
memset(tree,0,sizeof(tree));
Step 2:将下标为k的数加上val。
void update(int k,int val) {
while (k<=N) {
tree[k]+=val;
k+=k&(-k);
}
}
Step 3:查询区间[1,k]的和。
int query(int k) {
int sum=0;
while (k>0) {
sum+=tree[k];
k-=k&(-k);
}
return sum;
}
如果要询问区间[l,r]的和,那么答案就是query(r)-query(l-1)。
int find_Kth(int k,int N) {
int now=0;
for (int i=20;i>=0;i--) {
now|=(1<<i);
if (now>N || tree[now]>=k)
now^=(1<<i);
else k-=tree[now];
}
return now+1;
}