POJ 1990 (树状数组入门)

MooFest

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

题意:有n只牛,这n只牛分布在一行上,每个牛的位置是唯一的,不包含两只牛在同一个地方的情况,每一只牛都有一个threshold v(i) 。n只牛之间要相互通信联系,那么N(N-1)/2次配对,每次配对需要计算一个volume;假设i与j需要联系,那么i与j之间的volume=abs(i的位置-j的位置*max(v(i),v(j));
问配对完N(N-1)/2次,总的volume是多少。

分析:
直接暴力是做不出的,因为n的最大值是2e5,n2的复杂度;

我们暴力的目的就是要去判断相互比较的i与j谁的v值大,那么我们为何不先对v值进行排序呢;

struct node{
    int id;
    ll val;
    bool operator < (const node &a) const{
        return val<a.val;
    }
}b[N<<2];

 for(int i=1;i<=n;i++){
        scanf("%lld%d",&b[i].val,&b[i].id);
        ub=max(ub,b[i].id);//ub是上界
    }
    sort(b+1,b+1+n);

第一个步骤处理好了,但是距离怎么处理呢,不比较的话,还是不知道他们的距离差;
我们这样来想,如果能有一种方法可以O(n*logn)的复杂度去做就可以了,关键就在这个O(logn);

我们拿我们处理好的排序完的数据来看看;

排完序之后的情况是下面这个样子

v(i)2234
位置5613

我们拿6这个位置的牛来举例,假设当前只有位置为5和位置为6的牛在坐标轴上,那么我们可以很容易算出,两者之间的距离差是6-5=1* 6-5;
(为啥要写一个1* 6-5呢????)这就是问题的处理关键,我们称1* 6中的1为比6小的坐标的个数。

仔细想想如果我们知道,比i小的坐标个数,那么i与比它小的坐标之间的距离差=i的坐标*i小的坐标个数(num_small) - 比i小的坐标和(dis_small)—>是不是呢???

有人可能问了,知道比它小的距离差之和了,那么大的怎么求?

比i大的距离差 = i之间的所有的坐标和(tot) - 比i小的坐标和(dis_small) - 比i位置大的个数*i的位置;–>是不是呢?

 ll tot=0,ans=0;
    for(int i=1;i<=n;i++){
        ll num_small=Query(b[i].id,num);
        Update(b[i].id,num,1);
        
		//计算比当前i这个位置小的所有坐标差值和
        ll dis_small=num_small*b[i].id-Query(b[i].id,sum);
        Update(b[i].id,sum,b[i].id);

        ll dis_big=tot+dis_small-num_small*b[i].id-(i-1-num_small)*b[i].id;


        tot+=b[i].id;
        ans+=b[i].val*(dis_big+dis_small);
    }

这里主要的思路已经搭建好的话,那剩下的就是构建树状数组了,它可以在logn的时间复杂度下查询修改区间

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <set>
#include <utility>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
#define ms(arr) memset(arr,0,sizeof(arr))
//priority_queue<int,vector<int> ,greater<int> >q;
const int N = 1e5 + 10;
const int mod = 1e9+7;
ll sum[N<<2];//统计比i位置小的所有的位置差之和
ll num[N<<2];//统计比i位置小的所有的位置个数
int ub;
int lowbit(int x){
    return x&(-x);
}
ll Query(int x,ll a[]){
    ll ans=0;
    while(x){
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}
void Update(int x,ll a[],ll val){
    while(x<=ub){
        a[x]+=val;
        x+=lowbit(x);
    }
}
struct node{
    int id;
    ll val;
    bool operator < (const node &a) const{
        return val<a.val;
    }
}b[N<<2];
int main()
{
//    #ifndef ONLINE_JUDGE
//    freopen("in.txt","r",stdin);
//    #endif // ONLINE_JUDGE
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld%d",&b[i].val,&b[i].id);
        ub=max(ub,b[i].id);
    }
    sort(b+1,b+1+n);
    ll tot=0,ans=0;
    for(int i=1;i<=n;i++){
        ll num_small=Query(b[i].id,num);
        Update(b[i].id,num,1);

        ll dis_small=num_small*b[i].id-Query(b[i].id,sum);
        Update(b[i].id,sum,b[i].id);

        ll dis_big=tot+dis_small-num_small*b[i].id-(i-1-num_small)*b[i].id;

        tot+=b[i].id;
        ans+=b[i].val*(dis_big+dis_small);
    }
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值