树状数组

最近在看树状数组

树状数组在我的理解来看就是 找一种最好的方案使数组之间的排列在修改和查询时的复杂度最低 而树状数组就巧妙利用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
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值