MooFest【树状数组的妙用】【前缀和】

  题目是这样的:由于参加MooFest这个会议,使得有些奶牛耳聋了,我们现在就是想要知道要多少的声量使得所有牛两两之间都能听清,题目给出vixi分别代表最先听清所需的声量、这头奶牛在坐标上的位移。而题目让我们求得是,所需要的总和,也就是全体max(vi, vj)*abs(xi-xj)之和。

题面(后附上思路):

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

  思路

  我们要求所有的值,既然要比较vi与vj的较大值,不妨就按照v的大小,按照升序排列,这样在vi之前的元素,他们要与vi建立联系,肯定是以vi的大小作为基底的。

接下来,我们对其中一个vi作为参考系来进行比较,我们接下来就要考虑的是xi的关系,对于xi,在其之前的元素,有比它的,也有比它的(就是不可能有相等的)  ,这个时候可以把xi之前的那些{x}集合分成比xi小的以及比xi大的。那些比xi小的{x}可以用树状数组找到其个数a以及总量b,那么小于xi 的集合总大小就是a*xi-b;接下来,考虑大于xi的{x}集合,因为我们现在知道xi,那么就知道它是第i位,在此之前有(i-1)个元素,我们可以先弄一个前缀和,求得在此之前的到此位且不包含此位的元素的大小总和len[i],然后计算len[i]-b-(i-1-a)*xi即可求得大于xi的范围的总和。

完整代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
using namespace std;
typedef long long ll;
const int maxN=20005;
struct node
{
    ll v,x;
    node(ll a=0, int b=0) {v=a; x=b;}
}mp[maxN];
int N;
ll bita[maxN];      //用以记录小于x的点的数目,那么,我们接下来只需要知道小于x点的点的距离和即可
ll bitlen[maxN];    //记录下小于x的点的距离和,然后找到x之前元素的全体的前缀和即可求解关于i点的vi需求
ll qzsum[maxN];     //前缀和
bool cmp(node e1, node e2)
{
    return e1.v==e2.v?(e1.x<e2.x):(e1.v<e2.v);
}
void update_a(ll i, ll x)     //点数量的更新方式是把(xj, 1)放进去
{
    while(i<=maxN)
    {
        bita[i]+=x;
        i+=lowbit(i);
    }
}
void update_len(ll i, ll x)   //长度的更新方式是把(xj, xj)放进去,然后求的时候是求Query_len(xi)
{
    while(i<=maxN)
    {
        bitlen[i]+=x;
        i+=lowbit(i);
    }
}
ll Query_a(ll i)
{
    ll res=0;
    while(i)
    {
        res+=bita[i];
        i-=lowbit(i);
    }
    return res;
}
ll Query_len(ll i)
{
    ll res=0;
    while(i)
    {
        res+=bitlen[i];
        i-=lowbit(i);
    }
    return res;
}
int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        ll ans=0;      //这是最后所要求的答案
        memset(mp, 0, sizeof(mp));
        memset(bita, 0, sizeof(bita));      //计数的
        memset(bitlen, 0, sizeof(bitlen));  //记长的
        memset(qzsum, 0, sizeof(qzsum));    //记总长的
        for(int i=1; i<=N; i++)
        {
            scanf("%lld%lld",&mp[i].v,&mp[i].x);
        }
        sort(mp+1, mp+1+N, cmp);
        for(int i=1; i<=N; i++) qzsum[i]=qzsum[i-1]+mp[i-1].x;      //求出除本身点之前的所有点的前缀和
        update_a(mp[1].x, 1);
        update_len(mp[1].x, mp[1].x);
        for(int i=2; i<=N; i++)
        {
            ll a=Query_a(mp[i].x);
            ll b=Query_len(mp[i].x);
            ans+=(a*mp[i].x-b)*mp[i].v;
            ans+=(qzsum[i]-b-(i-1-a)*mp[i].x)*mp[i].v;
            update_a(mp[i].x, 1);
            update_len(mp[i].x, mp[i].x);
            //printf("%lld %lld\n",a,b);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/81329166