题目链接: 点击打开链接
该题是一道很巧妙的树状数组的应用,一开始初学ACM的时候曾经看别人题解做过,然而一丝作用也没有,完全没有理解该题,以至于现在见到和没做过一样。
个人认为学习树状数组还是去看一下lrj的蓝书吧,讲出了树状数组维护数据的原理,以及那个bit数组的含义,理解了数据结构原理之后才能随心所欲的运用。
该题求任意两个牛的吼叫值只和。 由于两头牛的吼叫值取最大的,所以一开始想将吼叫值从大到小排序,这样,后边的牛的吼叫值都不如前边的大,但是难以维护距离这个量。
那么怎么才能快速的维护距离这个变量呢? 先来看这样一个事实: 对于任意两头牛,他们之间的距离可以用位置之差间接求出,所以我们不妨维护两个量:牛的数量bit和牛的距离bit,这样,数量乘以当前牛的距离减去距离bit就是某一边的所有答案了,那么显然要按照吼叫值从小到大来进行,这样在树状数组中的都是比当前牛的吼叫值小的 。
由于每头牛的位置是唯一的,所以用位置做bit数组下标就好了。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<list>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 20000+3;
int T,n,y,cnt_bit[maxn+5],dist_bit[maxn+5];
struct point{
int v,x;
bool operator < (const point& rhs) const {
return v < rhs.v;
}
}a[maxn+5];
int sum(int* bit,int x) {
int ret = 0;
while(x > 0) {
ret += bit[x]; x -= (x & -x);
}
return ret;
}
void add(int* bit,int x,int d) {
while(x <= 20001) {
bit[x] += d; x += (x & -x);
}
}
int main() {
while(~scanf("%d",&n)) {
memset(cnt_bit,0,sizeof(cnt_bit));
memset(dist_bit,0,sizeof(dist_bit));
for(int i=0;i<n;i++) scanf("%d%d",&a[i].v,&a[i].x);
sort(a,a+n);
ll ans = 0;
for(int i=0;i<n;i++) {
ll l = sum(cnt_bit,a[i].x), r = sum(cnt_bit,maxn) - sum(cnt_bit,a[i].x);
ll dist_l = sum(dist_bit,a[i].x), dist_r = sum(dist_bit,maxn) - sum(dist_bit,a[i].x);
ans += ((a[i].v*(l*a[i].x - dist_l)) + (a[i].v*(dist_r - r*a[i].x)));
add(cnt_bit,a[i].x,1);
add(dist_bit,a[i].x,a[i].x);
}
printf("%I64d\n",ans);
}
return 0;
}