POJ 1990 MooFest

题目链接:POJ 1990

题意:有n头牛,在一条直线上,处于不同的坐标x,不同的听力值v(耳聋值),当i,j想要通话时,需要max(v(i),v(j))*(dist[i]-dist[j])的volume,问这n*(n-1)/2对牛总共的volume时多少。

题解:

首先是音量我们对这些牛按v值降序排列这样,每次当前牛和其前面的牛计算max(v(i),v(j))就等于当前牛的v值。

然后,计算每对的距离之和显然太耗时了,但是题目中给出的是坐标,我们可以将距离之和转化为坐标之和。

那么对于任意的一点i,i之前的牛的音量值肯定小于v[i],但是坐标的值可能比x[i]大也可能比x[i]小,因此我们应该分成两部分来考虑,就是小于xi的和大于xi的,也就是处在这头牛左边的牛和右边的牛。

首先考虑左边的情况,假设左边比小于等于v[i]的牛有三头坐标分别为a b c,那么左边的值就是v[i]*(x[i]-a)+v[i]*(x[i]-b)+v[i]*(x[i]-c) => v[i]*(3*x[i]-(a+b+c))。

那么假设左边有 l 头牛,坐标之和为sum,那么这头牛与左边牛的音量之和就是v[i]*(l*x[i]-sum);

这样就可以将距离转化为坐标之和,不用计算每对i,j的距离。只需要树状数组维护坐标之和即可。在用一个树状数组记录牛的个数。

右边同理。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int MAX=20000+5;
typedef long long LL;
int coor[MAX],cnt[MAX];
int N=20000;
struct node
{
    int v,x;
    node(int _v = 0 , int _x = 0):v(_v),x(_x){};
    bool operator <(const node &a) const
    {
        return v<a.v;
    }
};
node a[MAX];
LL sum(int *bit,int x)
{
    LL s=0;
    while(x>0)
    {
        s=s+bit[x];
        x-=(x&(-x));
    }
    return s;
}
void add(int *bit,int x,int v)
{
    while(x<=N)
    {
        bit[x]+=v;
        x+=(x&(-x));
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        memset(cnt,0,sizeof(cnt));
        memset(coor,0,sizeof(coor));
        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++)
        {
            ans=ans+1ll*a[i].v*(a[i].x*sum(cnt,a[i].x-1)-sum(coor,a[i].x-1));
            ans=ans+1ll*a[i].v*((sum(coor,N)-sum(coor,a[i].x))-a[i].x*(sum(cnt,N)-sum(cnt,a[i].x)));
            add(cnt,a[i].x,1);
            add(coor,a[i].x,a[i].x);
        }
        printf("%lld\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值