首页 > 代码库 > 奶牛集会(MooFest, USACO 2004 Open)

奶牛集会(MooFest, USACO 2004 Open)

题目背景

MooFest, 2004 Open

题目描述

约翰的N 头奶牛每年都会参加“哞哞大会”。哞哞大会是奶牛界的盛事。集会上的活动很

多,比如堆干草,跨栅栏,摸牛仔的屁股等等。它们参加活动时会聚在一起,第i 头奶牛的坐标为Xi,没有两头奶牛的坐标是相同的。奶牛们的叫声很大,第i 头和第j 头奶牛交流,会发出max{Vi; Vj}×|Xi ? Xj | 的音量,其中Vi 和Vj 分别是第i 头和第j 头奶牛的听力。假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。

输入输出格式

输入格式:

? 第一行:单个整数N,1 ≤ N ≤ 20000

? 第二行到第N + 1 行:第i + 1 行有两个整数Vi 和Xi,1 ≤ Vi ≤ 20000; 1 ≤ Xi ≤ 20000

输出格式:

? 单个整数:表示所有奶牛产生的音量之和

输入输出样例

输入样例#1:
4
3 1
2 5
2 6
4 3
输出样例#1:
57











注意到音量中的v值是取max的,

所以可以根据v从大到小的顺序给母牛排序进行处理


对于一个奶牛i,它对其他奶牛之间的传话有两种情况:

如果另一只奶牛j在它的右侧,发出的音量是Vi*(Xj-Xi),反之则是Vi*(Xi-Xj)




所以要求出奶牛i与其他所有的奶牛发出的音量和,就等于

Vi*(在奶牛i左侧的奶牛总数*奶牛i对应的坐标-左侧所有奶牛的坐标和)+Vi*(右侧所有奶牛的坐标和-在奶牛i右侧的奶牛总数*奶牛i对应的坐标)



因此这就需要用树状数组预处理出每个点的左侧的奶牛总数和它们的坐标和






技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 20005
#define ll long long
using namespace std;
struct Cow
{
    int x,v,xid;
}cow[MAXN];
struct Tree
{
    ll sum,num;
}c[MAXN];
int n;
bool cmpv(Cow a,Cow b){return a.v>b.v;}
bool cmpx(Cow a,Cow b){return a.x<b.x;}
ll ans=0;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&cow[i].v,&cow[i].x);
    sort(cow+1,cow+n+1,cmpx);
    for(int i=1;i<=n;i++)
    {
        cow[i].xid=i;
        for(int j=i;j<=n;j+=j&-j)
        {
            c[j].sum+=cow[i].x;
            c[j].num++;
        }
    }
    sort(cow+1,cow+n+1,cmpv);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ll sum=0,num=0;
        for(int j=cow[i].xid;j;j-=j&-j)
        {
            sum+=c[j].sum;
            num+=c[j].num;
        }
        ans+=(num*cow[i].x-sum)*cow[i].v;
        sum=-sum;num=-num;
        for(int j=n;j;j-=j&-j)
        {
            sum+=c[j].sum;
            num+=c[j].num;
        }
        ans+=(sum-num*cow[i].x)*cow[i].v;
        for(int j=cow[i].xid;j<=n;j+=j&-j)
        {
            c[j].sum-=cow[i].x;
            c[j].num--;
        }
    }
    printf("%lld",ans);

}
View Code

 

 


奶牛集会(MooFest, USACO 2004 Open)