最近在看树状数组
树状数组在我的理解来看就是 找一种最好的方案使数组之间的排列在修改和查询时的复杂度最低 而树状数组就巧妙利用lowbit 来使得分配的数组操作次数最少。
比方说单点修改,你不需要按自然数顺序修改所有的前缀数组,你只需要按lowbit顺序修改logn个数组 。查询同理。
下面贴上树状数组的模板,还是挺好写的。
int lowbit(int x) {return x&(-x);}
void add(int x,int k){
while(x>=n){
c[x]+=k; //其中c[x]为前缀和,可以调用n次add操作对c[i]进行初始化
x+=lowbit(x) //支持加减异或操作
}
}
int query(int x){
int sum=0;
while(x>0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
后面发现能用树状数组求逆序对
其实不难理解,举个例子,排列 5 4 3 2 1 中 当你插入5的时候你就给下标为5的数组加1,这个时候你就看你后面有没有为1的,如果有就是比你大的而且是先插入的,所以是逆序。求逆序个数转化为 i (原排列的第几个) 减去 你前面为1 的数组个数 ,其实就是树状数组的查询操作,查询前 i 个数之和 。
例题 poj 的 1804 就是一个典型的求逆序对的题目。
题目虽然说得是交换相邻的两个的次数,但是不难证明,逆序对为多少就需要交换多少次来达到顺序。
这个题要用到离散化的技巧,因为有些数字很大或者为0或者为负数,不方便存放在树状数组中,需要我们进行适当的转换,即保持原有数组的相对大小,而用其它方式代替。本题也只关系两个数之间的相对大小,所以有一个细节就是将原数组按数字大小进行排序,拍完后将原数组次序作为新数组下标。
更新(树状数组线性初始化)可达到O(N)复杂度
c[i] 保存的是 a[ i-lowbit(i)+1 ~ i] 这段区间的和,所以在初始化的时候不需要调用n次单点修改函数来初始化c[i] . 只需要在初始化的时候求前缀和来求出c[i] 即可
上代码
void init(){
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+a[i];
c[i]=pre[i]-pre[i-lowbit(i)];//前缀和之差,所以要减1
}
}