首页 > 代码库 > 【洛谷P2345】奶牛集会

【洛谷P2345】奶牛集会

题目背景

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

说明

朴素O(N2)

类似于归并排序的二分O(N logN)

树状数组O(N logN)

知识点

树状数组

分析

朴素算法O(N2)能过去一部分点。

题目上给的公式max{Vi; Vj}×|Xi ? Xj |。

我们可以先按照V进行从小到大排序,这样就可以排除V的干扰,也就是保证当前的Vi是最大的。

就可以用树状数组计算前缀和了。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20000+5;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1; ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
int n;
int c1[maxn],c2[maxn];
int mx,mn;
struct node
{
    int v,x;
    bool operator < (const node &j) const {
        return v<j.v;
    }
}e[maxn];
void update1(int x,int d)
{while(x<=mx){c1[x]+=d;x+=x&(-x);}}
void update2(int x,int d)
{while(x<=mx){c2[x]+=d;x+=x&(-x);}}
ll ask1(int x)
{ll ans=0;while(x){ans+=c1[x];x-=x&(-x);}return ans;}
ll ask2(int x)
{ll ans=0;while(x){ans+=c2[x];x-=x&(-x);}return ans;}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        e[i].v=read();e[i].x=read();
        mx=max(mx,e[i].x);
    }
    sort(e+1,e+n+1);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int x=e[i].x,v=e[i].v;
        ll num1,num2,sum1,sum2;
        num1=ask1(x-1);sum1=ask2(x-1);
        num2=ask1(mx)-ask1(x);
        sum2=ask2(mx)-ask2(x);
        ans+=v*(sum2-x*num2+x*num1-sum1);
        update1(x,1);
        update2(x,x);
    }
    printf("%lld\n",ans);
    return 0;
}

  

 

【洛谷P2345】奶牛集会