您的位置:首页 > Web前端

POJ 1990 MooFest(树状数组)

2015-09-22 16:08 585 查看
题目链接: 点击打开链接

该题是一道很巧妙的树状数组的应用,一开始初学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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: