POJ 1990 MooFest(树状数组)

超级传送门:http://poj.org/problem?id=1990


题意:X轴上有N头牛(1<=N<=20000),每头牛有一个听力值v[i]和唯一的横坐标x[i]。两头牛直接互相通话的花费定义为max(v[i],v[j])*abs(x[i]-x[j]),现在要求输出所有牛互相通话的总花费。

分析:用两个树状数组,一个用来存放当前位置左侧牛的个数,另一个存放左侧牛的横坐标。利用排序可以巧妙地避开max(v[i],v[j])的计算,先按照v从小到大对牛排序然后再依次插入树状数组,不影响结果。这样的话每次计算左侧的时候,乘以的v都是当前牛的v(当前最大)。

在纸上画个图就能看出其关系:

左侧牛到当前牛距离和 = 左侧牛数量 * X  -  左侧牛坐标和
右侧牛到当前牛距离和 = 右侧牛坐标和 - 右侧牛数量 * X


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 20010;
struct node {
    int x,v;
}cow[maxn];
int c1[maxn]; //树状数组1:记录牛的个数
int c2[maxn]; //树状数组2:记录牛的x坐标
int n;
int cmp(const struct node& a,const struct node& b) {
    if (a.v!=b.v) return a.v<b.v;
    return a.x<b.x;
}
int lowbit(int t) {
    return t & -t;
}
__int64 getsum1(int end) {
    __int64 ret=0;
    for (int i=end;i>=1;i-=lowbit(i)) {
        ret += c1[i];
    }
    return ret;
}
void update1(int p,int val) {
    for (int i=p;i<maxn;i+=lowbit(i)) {
        c1[i] += val;
    }
}
__int64 getsum2(int end) {
    __int64 ret=0;
    for (int i=end;i>=1;i-=lowbit(i)) {
        ret += c2[i];
    }
    return ret;
}
void update2(int p,int val) {
    for (int i=p;i<maxn;i+=lowbit(i)) {
        c2[i] += val;
    }
}
int main () {
    #ifndef ONLINE_JUDGE
        freopen("1.txt","r",stdin);
    #endif
    int n;
    __int64 ans;
    while (scanf("%d",&n)>0) {
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        ans = 0;
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&cow[i].v,&cow[i].x);
        }
        sort(cow+1,cow+n+1,cmp);
        for(int i=1;i<=n;i++) {
            update1(cow[i].x,1);
            update2(cow[i].x,cow[i].x);
            ans += cow[i].v*(  getsum1(cow[i].x-1)*cow[i].x //左侧牛数量 * X
                              -  getsum2(cow[i].x-1) //左侧牛坐标和
                             +  getsum2(maxn-1) - getsum2(cow[i].x) //右侧牛坐标和
                             - ( getsum1(maxn-1)-getsum1(cow[i].x) )*cow[i].x ); //右侧牛数量 * X
                             /*
                                左侧牛到当前牛距离和 = 左侧牛数量 * X  -  左侧牛坐标和
                                右侧牛到当前牛距离和 = 右侧牛坐标和 - 右侧牛数量 * X
                             */
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值