1 条题解
-
0
Solution:
其实我们在观察题目之后就会发现,绝对值最小为0,那么我们所求的答案,就是求所有绝对值相加最小得几。
如果学过一定的数学知识,就会知道两个数相减所得绝对值映射到几何中就是两个点的距离,我们会发现,对于一条数列上的数来说(题内的所有 在经过排序后可以形成一个数列),当 取在任意两个数之间时,这两个数对 作差,取绝对值的结果一定相同。
画图解释:

我们可以得出,+就是的长度,也就印证了上面的话。
同样的,我们也可以发现, 在 的左边或者 的右边,只会让答案变大,因此,我们可以知道,当有 个 时,若 为偶数,则当 取为 时答案最小。反之则为 时最小,可以轻易证明。
那么问题来了,如何求这个数呢?动态第 位数问题,如果每一次作一次sort,复杂度爆炸。
那么对于处理这个问题,有一个东西可以用来处理,那就是对顶堆。
对于对顶堆,我们首先得学会优先队列。
那么接下来我们来讲对顶堆。
我们观察一个有序的升序数组,可以发现,中位数前面的数均小于中位数,中位数后面的数均大于中位数,那么我们可以想像把一个数组切成两段,前面的数塞入大根堆,后面的数塞入一个小根堆,那么显然,两个堆的其中一个堆顶一定是中位数。
但是我为了方便,因此我选择让大根堆,即中位数之前的数组成的堆,的堆顶为中位数,即让大根堆的长度永远保持比小根堆更大,同时由于中位数前的数字必然比中位数后面更大,因此我也加了一个“swap”。
我们又犯愁了:所有 的和怎么算?对于每一个 都一个一个计算过去?当然,这会造成大面积TLE。
考虑到减数或者被减数均为上面求出来的中位数,因此我们不难想到,我们可以进行一个区间的操作,计算出大根堆中的区间和,再用区间长度乘上我们前面求出的中位数,减去,或者被减去这个区间的和,那么也就能够算出来当前的 了。
其他的细节可以结合代码一同看,也可以自己手动模拟对顶堆,拿几个小的数据模拟可能效果会更好些。
Code:
#include<bits/stdc++.h> #define int long long using namespace std; priority_queue<int, vector<int>, greater<int> > q; priority_queue<int> p; int n, ans, tmpp, tmp, cnt; inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = (x << 3) + (x << 1) + (c ^ '0'); c = getchar(); } return x * f; } signed main() { n = read(); for (register int i = 1;i <= n;++i) { int x, y, z; x = read(); if (x == 1) { y = read(); z = read(); p.push(y); tmpp += y; ans += z; if (p.size() - 1 > q.size())//如果大根堆的数字数量多于一半以上了,就要把多余的扔进小根堆保证大根堆堆顶为中位数 { int t = p.top(); p.pop(); q.push(t); tmpp -= t; tmp += t; } if (!q.empty() && p.top() > q.top())//保证大根堆中所有数小于小根堆中的数 { int a = p.top(); int b = q.top(); q.pop(); p.pop(); q.push(a); p.push(b); tmpp = tmpp - a + b; tmp = tmp + a - b; } } else { int x = p.top(); int y = (p.size() * x) - tmpp + (tmp - q.size() * x) + ans; printf("%lld %lld\n", x, y); } } return 0; }
- 1
信息
- ID
- 11659
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者