[ABC127F] Absolute Minima 题解

题意

现有一个函数 f ( x ) f(x) f(x),它最初是一个常数函数 f ( x ) = 0 f(x)=0 f(x)=0

现在要求按顺序处理 Q Q Q 个查询。有两种查询,更新查询和评估查询,如下所示:

  • 更新查询:给出两个整数 a a a b b b;令 g ( x ) = f ( x ) + ∣ x − a ∣ + b g(x)=f(x)+|x-a|+b g(x)=f(x)+xa+b,并将 f ( x ) f(x) f(x) 替换为 g ( x ) g(x) g(x)

  • 求值查询: 输出使 f ( x ) f(x) f(x) 最小化的 x x x,以及 f ( x ) f(x) f(x) 的最小值。如果有多个这样的 x x x 值,请选择最小值。

可以证明,求值查询中要输出的值始终是整数,因此要求将这些值打印为不带小数点的整数。

思路

首先,我们不难发现其实题目就是让我们求 ∣ x − a 1 ∣ + b 1 + ∣ x − a 2 ∣ + b 2 + … + ∣ x − a n ∣ + b n |x-a_1|+b_1+|x-a_2|+b_2+…+|x-a_n|+b_n xa1+b1+xa2+b2++xan+bn 的值最小。我们利用数轴可以发现,不管是任何情况,我们只需要满足 x x x 的值为 a 1 a_1 a1 a 2 a_2 a2 a 3 … a n a_3…a_n a3an 中的中位数就行了。

我们可以使用对顶堆,用一个大顶堆和一个小顶堆动态维护中位数。我们在插入的时候只需要满足数据个数是偶数,那我们要在大根堆中新插入一个数,否则放入小根堆中。插入完之后,我们再对堆进行维护,实现了 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的时间复杂度,符合题目。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
priority_queue<int, vector<int>, greater<int> > small;//小根堆
priority_queue<int, vector<int>, less<int> > big;//大根堆
int q;
int sum;
int a[2000005], cnt;
int ans = 0;
signed main() {
    cin >> q;
    int op;
    while (q--) {
        scanf("%lld", &op);
        if (op == 1) {//更新查询
            int b;
            scanf("%lld%lld", &a[++cnt], &b);
            sum += b;
            if (cnt % 2 == 1) {
                big.push(a[cnt]);//放入大根堆
            } else {
                small.push(a[cnt]);//放入小根堆
            }
            int Y = big.top();//记录原本的中位数
            while ((small.size()) && (small.top() < big.top())) {//维护堆
                int x = big.top(), y = small.top();//将两个堆顶互换
                big.pop();
                small.pop();
                big.push(y);
                small.push(x);
            }
            if (Y == big.top()) {//中位数没发生改变
                ans += abs(a[cnt] - Y);//直接加上当前的b
            } else if (cnt % 2 == 1) {
                ans += abs(big.top() - Y);//加上两个中位数的差
            } else if (cnt % 2 == 0) {
                ans += abs(a[cnt] - Y);//直接加上当前的b
            }
        } else {//求值查询
            printf("%lld %lld\n", big.top(), ans + sum);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值