*点击

[USACO04OPEN]MooFest

题目

Description

约翰的N 头奶牛每年都会参加“哞哞大会”。哞哞大会是奶牛界的盛事。集会上的活动很多,比如堆干草,跨栅栏,摸牛仔的屁股等等。它们参加活动时会聚在一起,第i 头奶牛的坐标为Xi,没有两头奶牛的坐标是相同的。奶牛们的叫声很大,第i 头和第j 头奶牛交流,会发出max{Vi; Vj}×|Xi − Xj | 的音量,其中Vi 和Vj 分别是第i 头和第j 头奶牛的听力。

假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。

Input

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

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

Output

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

Sample Input

4
3 1
2 5
2 6
4 3

Sample Output

57

思路

首先肯定想到按坐标先排序;

我们可以枚举每个点假设作为 $max(v[i],v[j])$ 较大的那一个;

然后用树状数组分别找出前面 $\leq v[i]$ 的牛 和 后面  $\leq v[i]$ 小的牛;

分别加上音量即可;

那么求距离需要开两个树状数组,一个存 $\leq v[i]$ 的个数,另一个存 $\leq v[i]$ 的坐标和;

$i$ 与所有点的距离和就是 ,个数$\times v[i]$ $-$ 坐标和 ;

注意这里有个魔鬼细节:

可能会出现了重复的 $v[i]$ ;

假设两个点坐标是 $i$ ,$j$ $( i \leq j)$;

这样可能导致找前面 $\leq v[j]$ 时计算了一遍了 $v[i]$ ; 找后面 $\leq v[i]$ 时计算了一遍 $v[j]$ ;

那么解决的话,就在找前面 或 找后面时 改成求 $<v[i]$ 就可以了;

 

写完后,我看了一下其他巨佬的题解;

写法不太一样,瞬间觉得我的写法弱爆了;

 

代码

#include<bits/stdc++.h>
#define re register
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n;
ll mx=-1;
struct ljj
{
    ll x,v;
}a[50010];
struct ljq
{
    ll w,num;
}sum[50010];
inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void insert(ll x,ll y)
{
    while(x<=mx)
    {
        sum[x].w+=y;
        x+=lowbit(x);
    }
}
inline ll findout(ll x)
{
    ll s=0;
    while(x)
    {
        s+=sum[x].w;
        x-=lowbit(x);
    }
    return s;
}
inline void add(ll x,ll y)
{
    while(x<=mx)
    {
        sum[x].num+=y;
        x+=lowbit(x);
    }
}
inline ll find(ll x)
{
    ll s=0;
    while(x)
    {
        s+=sum[x].num;
        x-=lowbit(x);
    }
    return s;
}
inline ll cmp(ljj a,ljj b)
{
    if(a.x==b.x)
        return a.v<b.v;
    else
        return a.x<b.x;
}
int main()
{
    n=read();
    for(re ll i=1;i<=n;i++)
    {
        a[i].v=read();
        a[i].x=read();
        mx=max(a[i].v,mx);
    }
    sort(a+1,a+n+1,cmp);//按坐标排序 
    ll ans=0;
    for(re ll i=1;i<=n;i++)
    {
        ll sum=findout(a[i].v);//计算前面小于等于 v[i] 的坐标和 
        ll t=find(a[i].v);     //找小于等于 v[i] 的个数 
        insert(a[i].v,a[i].x);
        add(a[i].v,1);
        ans+=abs(t*a[i].x-sum)*a[i].v; //所有坐标相减 乘以 v[i] 
    }
    memset(sum,0,sizeof(sum));
    for(re ll i=n;i>=1;i--)
    {
        ll sum=findout(a[i].v-1);//计算后面小于 v[i] 的坐标和 
        ll t=find(a[i].v-1);     //找小于 v[i] 的个数 
        insert(a[i].v,a[i].x);
        add(a[i].v,1);
        ans+=abs(t*a[i].x-sum)*a[i].v; //所有坐标相减 乘以 v[i] 
    }
    printf("%lld\n",ans);
    //return 0; 
}

 

posted @ 2021-02-03 11:43  木偶人-怪咖  阅读(73)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录