P5094 [USACO04OPEN]MooFest

题目描述

每一年,约翰的N只奶牛参加奶牛狂欢节。这是一个全世界奶牛都参加的大联欢。狂欢节包括很多有趣的活动,比如干草堆叠大赛、跳牛栏大赛,奶牛之间有时还相互扎屁股取乐。当然,她们会排成一列嚎叫,来欢庆她们的节日。奶牛们的叫声实在刺耳,以致于每只奶牛的听力都受到不同程度的损伤。现在告诉你奶牛i的听力为vi ,这表示如果奶牛j想说点什么让她听到,必须用高于vi​×dis(i,j) 的音量。因此,如果奶牛i和j想相互交谈,她们的音量必须不小于max(vi​,vj​)×dis(i,j)。其中 dis(i,j) 表示她们间的距离。
现在N只奶牛都站在一条直线上了,每只奶牛还有一个坐标xi​。如果每对奶牛都在交谈,并且使用最小音量,那所有N(N-1)/2对奶牛间谈话的音量之和为多少?

输入格式

第1行输入一个整数N。
接下来N行,每行输入两个数vi​和xi​,分别代表第i头奶牛的听力和坐标。

输出格式

输出一个数,代表这N(N-1)/2对奶牛谈话时的音量之和。

样例

输入
4
3 1
2 5
2 6
4 3
输出
57

题目分析

我们首先可以给a[]按v从小到大排序,这样计算时每次取出的max(v)就一定是当前的a[i].v。
我们可以维护两个树状数组,分别维护一段序列中坐标的个数和坐标位置的和。
然后从左向右枚举所有的数,每次算出第i头牛和前面所有牛对话时的音量和,再把第i头牛的信息放入树状数组中。 最后将这些音量和相加即可,得到答案。这个题的难点也就是在每次求音量和这里。
要想求出第i头牛和前面所有牛对话时的音量和,需要三个变量:num为i-1头牛中,坐标位置在第i头牛前面的牛的数量。sum为这num头牛的坐标和。k为这i-1头牛的坐标和。这三个变量的前两个可以用前面所维护的树状数组来算出。
然后计算这个音量和:它可以分为两个部分

  1. 坐标在a[i].x前面的。ans+=(a[i].x*num-sum)*a[i].v //第i头牛的坐标*num-sum即为前面所有牛与第i头牛的坐标差的和,再乘a[i].v即为音量
  2. 坐标在a[i].x后面的。ans+=((k-sum)-(i-1-num)*a[i].x)*a[i].v //k-sum为坐标在第i头牛后面的所有牛的坐标和,i-1-num为坐标在第i头牛后面的所有牛的数量,它再乘a[i].x然后与k-sum相减后为即为后面所有牛与第i头牛的坐标差的和

两者相加即为第i头牛与前面i-1头牛的音量和,最后还要将第i头牛的信息放入树状数组中去。

代码如下
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
#define LL long long
using namespace std;
const int N=5e4+10;
struct Node{
    int v,x;
    bool operator< (const Node &a) const
    {
        return v<a.v;
    }
}a[N];
int n;
int tr1[N],tr2[N];  //tr1[]维护数量和,tr2[]维护坐标和
int lowbit(int x)	//树状数组三个模板函数
{
    return x & -x;
}
void add(int tr[],int x,int k)
{
    for(int i=x;i<N;i+=lowbit(i))
        tr[i]+=k;
}
int query(int tr[],int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=tr[i];
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d %d",&a[i].v,&a[i].x);
    
    sort(a+1,a+1+n);	//按v排序
    LL ans=0,k=0;
    for(int i=1;i<=n;i++)
    {
        int num=query(tr1,a[i].x);
        int sum=query(tr2,a[i].x);
        ans+=(LL)(a[i].x*num-sum)*a[i].v;
        ans+=(LL)((k-sum)-(i-1-num)*a[i].x)*a[i].v;
        
        add(tr1,a[i].x,1);
        add(tr2,a[i].x,a[i].x);
        k+=a[i].x;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值