利用树状数组去绝对值
树状数组学习链接: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 -=