题意
现有一个函数 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)+∣x−a∣+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 ∣x−a1∣+b1+∣x−a2∣+b2+…+∣x−an∣+bn 的值最小。我们利用数轴可以发现,不管是任何情况,我们只需要满足 x x x 的值为 a 1 a_1 a1, a 2 a_2 a2, a 3 … a n a_3…a_n a3…an 中的中位数就行了。
我们可以使用对顶堆,用一个大顶堆和一个小顶堆动态维护中位数。我们在插入的时候只需要满足数据个数是偶数,那我们要在大根堆中新插入一个数,否则放入小根堆中。插入完之后,我们再对堆进行维护,实现了 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;
}