poj 1990 MooFest(树状数组)

MooFest
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 6265 Accepted: 2765

Description

Every year, Farmer John's N (1 <= N <= 20,000) cows attend "MooFest",a social gathering of cows from around the world. MooFest involves a variety of events including haybale stacking, fence jumping, pin the tail on the farmer, and of course, mooing. When the cows all stand in line for a particular event, they moo so loudly that the roar is practically deafening. After participating in this event year after year, some of the cows have in fact lost a bit of their hearing. 

Each cow i has an associated "hearing" threshold v(i) (in the range 1..20,000). If a cow moos to cow i, she must use a volume of at least v(i) times the distance between the two cows in order to be heard by cow i. If two cows i and j wish to converse, they must speak at a volume level equal to the distance between them times max(v(i),v(j)). 

Suppose each of the N cows is standing in a straight line (each cow at some unique x coordinate in the range 1..20,000), and every pair of cows is carrying on a conversation using the smallest possible volume. 

Compute the sum of all the volumes produced by all N(N-1)/2 pairs of mooing cows. 

Input

* Line 1: A single integer, N 

* Lines 2..N+1: Two integers: the volume threshold and x coordinate for a cow. Line 2 represents the first cow; line 3 represents the second cow; and so on. No two cows will stand at the same location. 

Output

* Line 1: A single line with a single integer that is the sum of all the volumes of the conversing cows. 

Sample Input

4
3 1
2 5
2 6
4 3

Sample Output

57

Source

USACO 2004 U S Open


题目大意:一群牛参加完牛的节日后都有了不同程度的耳聋,第i头牛听见别人的讲话,别人的音量必须大于v[i],当两头牛i,j交流的时候,交流的最小声音为max{v[i],v[j]}*他们之间的距离。现在有n头牛,求他们之间两两交流最少要的音量和。

解题思路:首先要根据音量f进行排序,这样就可以进行优化,即对于某一头牛i来说,排在他前面的音量都比他小,肯定是用i自身的音量*两者之间的距离。此时的计算公式为:(求解比当前x小的所有和)

(x-x1)*f;

(x-x2)*f;

(x-x3)*f........

综上为:(n*x(x1+x2+x3+.....))*f; 注释:x表示某一头牛当前的位置,x1,x2,x3......等表示排在他前面的牛的位置,f当前这头牛的音量,因为他是最大的音量。(这里的距离我们需要采用树状数组进行求解)

这里有个地方需要解释一下,就是排在这个牛i前面的音量一定都比他小,但是坐标值x不一定比他小,所以我们只能分开处理。

我们可以在建立一个数轴,一个点一个点的放进去,先放进去的音量肯定是小的,把他按照该有的固定位置放进去,这样排在他前面的所有x都比他小就可以按照上面的方法采用树状数组的方法进行区间求和。(这里要放入一个求解一次他左边和右边的值,左边就是比当前x小的,右边是比当前x大的)。

下面介绍比当前值x大的情况计算方法:

(x1-x)*f;

(x2-x)*f;

(x3-x)*f........

综上:((x1+x2+x3+......)-n*x)*f;

这里的n值就应该等于放进去的所有牛的个数-前面已经计算过的比他x小的个数:i-nn;

(x1+x2+x3+...)就应该等于总和减去比他小的x的和。

要用long long !!!

这样讲起来很复杂,详情请看代码实现~~~~不懂得评论吐舌头


详见代码。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define ll long long

struct node
{
    ll f,x;
} s[20010];

int n;
ll c1[20010],c2[20010];
ll a1[20010],a2[20010];

bool cmp(node a,node b)
{
    return a.f<b.f;
}

ll lowbit(ll k)
{
    return k&(-k);
}

void add1(ll k,ll num)
{
    while (k<20010)
    {
        c1[k]+=num;
        k+=lowbit(k);
    }
}

void add2(ll k,ll num)
{
    while (k<20010)
    {
        c2[k]+=num;
        k+=lowbit(k);
    }
}

ll sum1(ll k)
{
    ll sm=0;
    while (k)
    {
        sm+=c1[k];
        k-=lowbit(k);
    }
    return sm;
}

ll sum2(ll k)
{
    ll sm=0;
    while (k)
    {
        sm+=c2[k];
        k-=lowbit(k);
    }
    return sm;
}

int main()
{
    while (~scanf("%d",&n))
    {
        for (int i=1; i<=n; i++)
        {
            scanf("%lld%lld",&s[i].f,&s[i].x);
        }
        memset(a1,0,sizeof(a1));
        memset(a2,0,sizeof(a2));
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        sort(s+1,s+n+1,cmp);
        ll ans=0;
        for (int i=1; i<=n; i++)
        {
            a1[s[i].x]++;//a1数组是用来表示每个位置有多少头牛
            a2[s[i].x]+=s[i].x;//a2数组是用来表示每头牛的坐标,方便求解坐标和
            add1(s[i].x,1);
            add2(s[i].x,s[i].x);
            ll nn=sum1(s[i].x);//个数
            ll sss=sum2(s[i].x);//坐标
            //cout<<111111<<endl;
            ans+=(nn*s[i].x-sss)*s[i].f;//得到的是在当前x前面所有坐标小于他的牛,此时我们还需要找到在他前面的比他坐标值大的x

            ll nnn=i-nn;
            ll ss=sum2(20000)-sss;
            ans+=(ss-s[i].x*nnn)*s[i].f;
        }
        printf ("%lld\n",ans);
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值