AcWing[P265]营业额统计

首先看题面,给出一个数 n n n n n n个数 a i ( 1 ≤ i ≤ n ) a_i(1≤i≤n) ai(1in),求

$\sum_{i=1}^{n} min_{(1≤j<i)}\left | a_i - a_j \right | $

可以直接暴力,每次从 1 1 1 i i i遍历,遍历 n n n遍,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

但这里 n n n的数据范围为 1 ≤ n ≤ 32767 1≤n≤32767 1n32767, 3276 7 2 = 1073676289 32767^2 = 1073676289 327672=1073676289,显然会超时

所以我们就要用一种能查询 a j − a i a_j - a_i ajai的最小值最方便的数据结构,这显然是平衡树~~(当然是看算法标签看出来的)~~

手写平衡树"比较"麻烦,但C++中的STL数据模板库中有为我们封装好的平衡树(用 v e c t o r vector vector存)

设当前数组中输入到的数为 x x x v e c t o r vector vector数组命名为a则a.insert(upper_bound(a.begin(),a.end(),x),x);表示为将 x x x存入 a a a平衡树;*upper_bound(a.begin(),a.end(),x)表示为在 a a a平衡树中小于 x x x的最大值;*--lower_bound(a.begin(),a.end(),x)表示为在 a a a平衡树中大于 x x x的最小值,因此, m i n ( 1 ≤ j < i ) ∣ a i − a j ∣ min_{(1≤j<i)}\left | a_i - a_j \right | min(1ji)aiaj就可以表示为min(abs((*upper_bound(a.begin(),a.end(),x)) - x), abs(x - (*--lower_bound(a.begin(),a.end(),x))))

这样,每次输入 x x x,并将 x x x装入 a a a平衡树,并将 s u m sum sum每次加上min(abs((*upper_bound(a.begin(),a.end(),x)) - x), abs(x - (*--lower_bound(a.begin(),a.end(),x)))) s u m sum sum即为所求

但WA了8次的经验告诉我,这是错的

以输入样例为例,刚才的方法输出答案为 15 15 15,但标准答案为 12 12 12,这是因为 5 5 5出现了两次

注意,问题出来了,*upper_bound(a.begin(),a.end(),x)表示为在 a a a平衡树中小于 x x x的最大值;*--lower_bound(a.begin(),a.end(),x)表示为在 a a a平衡树中大于 x x x的最小值,但是,当在 a a a中有数字和 x x x数值相同呢?

题目中要求的是$\sum_{i=1}^{n} min_{(1≤j<i)}\left | a_i - a_j \right | ,故当 ,故当 ,故当a_i == a_j 时, 时, 时,min$为 0 0 0,此时 m i n min min为最小,但刚才的步骤把它略过了,所以可以再定义两个数组,将输入过的数字记上,若当前输入数字已被操作过的话,则不对其进行操作

注意:这里一定要设两个数组,因为数据范围中$ |a_i | ≤ 10^6 ,所以 ,所以 ,所以a_i$可能为负,两个数组一个记录正数,一个记录负数(当然也可以合并在一起)

但,样例还是没过

qwq

我们把 n n n次中每次的 s u m sum sum输出出来就可以发现:出问题了!第二个 s u m sum sum 6 6 6

再把 m i n min min中的两个数据输出出来,为 0 0 0 5 5 5

So,很容易就可以发现出,当 i = = 2 i == 2 i==2时没有比 1 1 1更小的数,故返回为 0 0 0,此时 m i n ( 1 , 4 ) = = 1 min(1, 4) == 1 min(1,4)==1,但 m i n min min左边的数据是不存在的,就需要存一个最大最小值,若 x x x为最大(小)值,则返回 m i n min min中左(右)边的数据,最后输出 s u m sum sum即可,这样我们就可以A了这道题

So~~(这道题坑点真多)~~

这里是代码↓↓↓:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
vector<int> a;
int n, x, sum, mi = INT_MAX, mx = INT_MIN;
bool z[N], f[N];
signed main() {
    scanf("%d", &n);
    memset(z, 0, sizeof z);
    memset(f, 0, sizeof f);
    for (int i = 1; i <= n; i ++ ) {
        scanf("%d", &x);
        mi = min(mi, x), mx = max(mx, x);
        a.insert(upper_bound(a.begin(),a.end(),x),x);
        int zx = abs((*upper_bound(a.begin(),a.end(),x)) - x);
        int zd = abs(x - (*--lower_bound(a.begin(),a.end(),x)));
        if ((x >= 0 && z[x] == 0) || (x < 0 && f[abs(x)] == 0)){
            if (x == mi) sum += zx;
            else if (x == mx) sum += zd;
            else sum += min(zx, zd);
        }
        if (x >= 0) z[x] = true;
        else f[abs(x)] = true;
    }
    printf("%d\n", sum);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值