树状数组去绝对值模型

本文介绍了如何利用树状数组高效地解决涉及`abs(a[i] - b[j])`的计算问题,通过两个经典题目的例子阐述了树状数组在处理这类问题中的应用和公式推导,并给出了相应的代码实现。
摘要由CSDN通过智能技术生成

利用树状数组去绝对值

树状数组学习链接:https://www.cnblogs.com/xenny/p/9739600.html
  在题目中经常会遇到一类含有项abs(a[i] - b[j])的问题,当i、j的数据范围过大时很容易出现超时的现象,于是如何在高效的时间复杂度的前提下计算该类模型成为了一个值得总结的问题。这里例举了两道经典的题目并附带了推导过程。

NO1

题目链接:MooFest POJ - 1990

题意: 每头牛都有一个坐标ax,和听力v,两头交谈时,消耗的为(abs(x[i]-x[j])*max( v[ i ], v[ j ] ))N头牛共交谈( N *(N-1))/2次,问共消耗多少。

公式推导: 首先n头牛中的两头牛交谈的代价要从v[i]和v[j]中取Max,可以想象到最大的v要用n - 1次,次大的v要用 n-2 次,这里看出肯定要对v进行排序,又因为坐标和v相对应,这里肯定要用结构体进行存储后再排序。
由此得到初始公式:
在这里插入图片描述
对于内层 j 循环来说 i 为常数,要计算内层的和,我们可以去掉绝对值分段讨论,假设比a[i].x小的a[j].x的数量为 ln,比a[i].x小的a[j].x的和为 lsum,比a[i].x大的a[j].x的数量为 rn,比a[i].x大的a[j].x的和为 rsum,我们可以将上图中的内层公式变换为 ln * a[i].x - lsum + rsum - rn * a[i].x,至于rn、ln、rsum、lsum如何求,很显然可以利用树状数组解决。

代码实现:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem( f, x ) memset( f, x , sizeof( f ))
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define mk(x, y) make_pair( x, y )
#define fi first
#define se second
using namespace std;
const int M = 20005;
const int N = 20005;
int n;
int num[N];
ll sum[N];
ll mx;

struct node{
   
    ll x, v;
    node( ){
    x = v = 0; }
    node( ll vv, ll xx ){
   
        x = xx, v = vv;
    }
    void scn( ){
   
        scanf( "%lld %lld", &v, &x );
    }
}p[N];

bool cmp( node p1, node p2 ){
   
    if( p1.v == p2.v )
        return p1.x < p2.x;
    return p1.v < p2.v;
}

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

void update( int i, ll x ){
   
    while( i <= mx ){
   
        num[i] += 1;
        sum[i] += x;
        i += lowbit( i );
    }
}

int getnum( int i ){
   
    int ret = 0;
    while( i > 0 ){
   
        ret += num[i];
        i -=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值