1 条题解

  • 0
    @ 2026-6-6 2:28:33

    Solution:

    其实我们在观察题目之后就会发现,绝对值最小为0,那么我们所求的答案,就是求所有绝对值相加最小得几。

    如果学过一定的数学知识,就会知道两个数相减所得绝对值映射到几何中就是两个点的距离,我们会发现,对于一条数列上的数来说(题内的所有 aa 在经过排序后可以形成一个数列),当 xx 取在任意两个数之间时,这两个数对 xx 作差,取绝对值的结果一定相同。

    画图解释:

    我们可以得出,xa|x-a|+xb|x-b|就是abab的长度,也就印证了上面的话。

    同样的,我们也可以发现, xxaa 的左边或者 bb 的右边,只会让答案变大,因此,我们可以知道,当有 cntcntaa 时,若 cntcnt为偶数,则当 xx 取为 acnt/2a_{cnt/2}时答案最小。反之则为 acnt/2+1a_{cnt/2+1}时最小,可以轻易证明。

    那么问题来了,如何求这个数呢?动态第 kk 位数问题,如果每一次作一次sort,复杂度爆炸。

    那么对于处理这个问题,有一个东西可以用来处理,那就是对顶堆。

    对于对顶堆,我们首先得学会优先队列。

    那么接下来我们来讲对顶堆。

    我们观察一个有序的升序数组,可以发现,中位数前面的数均小于中位数,中位数后面的数均大于中位数,那么我们可以想像把一个数组切成两段,前面的数塞入大根堆,后面的数塞入一个小根堆,那么显然,两个堆的其中一个堆顶一定是中位数。

    但是我为了方便,因此我选择让大根堆,即中位数之前的数组成的堆,的堆顶为中位数,即让大根堆的长度永远保持比小根堆更大,同时由于中位数前的数字必然比中位数后面更大,因此我也加了一个“swap”。

    我们又犯愁了:所有 f(x)f(x) 的和怎么算?对于每一个 aa 都一个一个计算过去?当然,这会造成大面积TLE。

    考虑到减数或者被减数均为上面求出来的中位数,因此我们不难想到,我们可以进行一个区间的操作,计算出大根堆中的区间和,再用区间长度乘上我们前面求出的中位数,减去,或者被减去这个区间的和,那么也就能够算出来当前的 f(x)f(x) 了。

    其他的细节可以结合代码一同看,也可以自己手动模拟对顶堆,拿几个小的数据模拟可能效果会更好些。

    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
    上传者