最近学习了一下树状数组,这道题纠结了很久,终究是因为没有明白树状数组怎么用。
感觉网上许多大神都只是讲原理,对于我们这些初学的菜鸟恐怕都被吓跑了。
这里我就以实用主义说一下使用方法(其实我觉得其原理应该能对我们更有启发,也许会带来很多潜在的好处):
这里需要注意的是,bit的实现代码中的bit数组一开始必须清零,这个数组并不是用来储存元素的,而是为实现这个数据结构而存在的。 你需要存储的元素是要通过那个add函数添加的,而求和则是要通过sum函数实现的,而这个bit数组的结构并不是对于一个新手很容易理解的,我们也大可不必关心这个。
另外要注意一个地方,在add函数里那个n,要开的足够大,不然你是添加不进去的,比如你要是把n=10,求sum(100) 会得到0 。
举个例子: 输入n个元素,求前n个元素的和。代码如下
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int bit[1000],n;
int sum(int i) {
int s = 0;
while(i>0) {
s+=bit[i];
i-=i& -i;
}
return s;
}
int add(int i,int x){
while(i <= n){
bit[i] += x;
i += (i & -i);
}
}
int main() {
while(~scanf("%d",&n)) {
memset(bit,0,sizeof(bit));
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
add(i,x);
}
for(int i=1;i<=n;i++) printf("%d\n",sum(i));
}
return 0;
}
相信看了上面的代码就可以很清晰的明白了,第几个元素,是要输入进add函数的,你输入的i是几,那么这个元素就是树状数组中的第几个元素。与bit数组无关,这个数组只是为了实现特定的数据结构而存在的辅助函数罢了。
该题代码是参考了别人的。。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int max_n = 20000+4;
ll cnt_bit[20005]={0},distance_bit[20005]={0};
int n;
struct point {
int v,x;
}a[20005];
bool cmp(point a,point b) {
return a.v<b.v;
}
ll sum_(ll *bit,int i) {
ll s = 0;
while(i>0) {
s+=bit[i];
i-=i & -i;
}
return s;
}
ll sum(ll *a,int i,int j) {
return sum_(a,j-1) - sum_(a,i-1);
}
void add(ll *bit,int i,int x) {
while(i<=max_n) {
bit[i] += x;
i += i&-i;
}
}
int main() {
ll tot = 0;
scanf("%d",&n);
memset(cnt_bit,0,sizeof(cnt_bit));
memset(distance_bit,0,sizeof(distance_bit));
for(int i=0;i<n;i++) scanf("%d%d",&a[i].v,&a[i].x);
sort(a,a+n,cmp);
for(int i=0;i<n;i++) {
int v = a[i].v;
int x = a[i].x;
ll left = sum(cnt_bit,1,x) ;
ll right = sum(cnt_bit,x+1,max_n);
tot+=((left*x-sum(distance_bit,1,x)) + (sum(distance_bit,x+1,max_n)-right*x))*v;
add(cnt_bit,a[i].x,1);
add(distance_bit,a[i].x,a[i].x);
}
printf("%lld\n",tot);
return 0;
}