Loading

【题解】CF1268C K Integers

萌新不懂就问,这是什么时代的题啊???

思路

trick 题。

首先根据 trick 可知:先将 \([1, k]\) 中的数聚在一起再排序是最优的。

排序的花费是逆序对数,所以现在的问题是求把 \([1, k]\) 聚在一起的最小花费。

又根据经典 trick 可知把它们聚在中位数的位置一定是最优的。

假设中位数为 \(t\),如果 \(t\) 左边有 \(x\) 个数,那么左侧移动的花费是 \(\sum\limits_{i = 1}^x t - i + 1 - p_i = tx - \frac{x(x - 1)}{2} - \sum\limits_{i = 1}^x p_i\)

同理右侧的花费是 \(\sum\limits_{i = 1}^x p_i - tx - \frac{x(x - 1)}{2}\)

那么只需要一个树状数组维护逆序对,另一个维护一下下标和即可。

这里直接 BIT 二分的复杂度是 \(O(n \log n)\),但是我写了两只 \(\log\).

代码

#include <cstdio>
using namespace std;

typedef long long ll;

const int maxn = 2e5 + 5;

int n;
int p[maxn], a[maxn];

struct BIT
{
    ll c[maxn];

    int lowbit(int x) { return x & (-x); }

    void update(int p, int w) { for (int i = p; i <= n; i += lowbit(i)) c[i] += w; }

    ll query(int p)
    {
        ll res = 0;
        for (int i = p; i; i -= lowbit(i)) res += c[i];
        return res;
    }
} c1, c2;

int main()
{
    ll cur = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &p[i]);
        a[p[i]] = i;
    }
    for (int i = 1; i <= n; i++)
    {
        cur += (i - 1 - c1.query(a[i]));
        c1.update(a[i], 1);
        c2.update(a[i], a[i]);
        int l = 1, r = n;
        while (l < r)
        {
            int mid = (l + r + 1) >> 1;
            if (c1.query(mid - 1) * 2 <= i) l = mid;
            else r = mid - 1;
        }
        ll lcnt = c1.query(l), lsum = c2.query(l);
        ll rcnt = i - lcnt, rsum = c2.query(n) - lsum;
        ll cost = l * lcnt - lsum - lcnt * (lcnt - 1) / 2;
        cost += rsum - l * rcnt - rcnt * (rcnt + 1) / 2;
        printf("%lld ", cur + cost);
    }
    putchar('\n');
    return 0;
}
posted @ 2023-01-11 18:41  kymru  阅读(17)  评论(0编辑  收藏  举报