Page Top

P2448 无尽的生命 题解

简要分析

求逆序对,但是元素很多(\(\le 2^{31} - 1\)\(2 \times 10^9\) 多一点),由于题目给出的 \(k\) 却比较小,所以我们可以从 \(k\) 入手:

显而易见,\(2 \times 10^9\) 的元素中最多只会有 \(2 \times 10^5\) 个被交换(不会重复交换一个元素),而剩下的大量数据如果都储存下来,是没有意义的。

我们可以将没有参与交换的元素中,连续的部分看成一个整体,这里就用到了离散化的思想,将这些元素合并以后,剩下的元素就可以 \(O(n \log n)\) 求逆序对了。

逆序对

逆序对怎么求,这里我们可以用树状数组求。由于这个不是模板题,不再解释。(都是紫题了还要讲逆序对?

但是在这道题中,一部分元素是包含多个元素的,怎么办?

这就可以用到权值逆序对:我们在查找的过程中,现在发现了一个现在数组中的一对逆序对 \(S_i > S_j\)\(i < j\)),\(S_i\) 包含 \(t_i\) 个元素,\(S_j\) 包含 \(t_j\) 个元素。因为只有连续的区间才会被合并,所以 \(S_i\) 包含的所有元素都大于 \(S_j\) 中的任意元素。

因此,\(S_i\)\(S_j\) 对答案的贡献为 \(t_i \times t_j\)

细节和易错点

  1. 数组别开的太小,会 RE 的,我开到了 \(4 \times 10^5\)

  2. \(10^5\) 级别的数据,不开 long long 怎么行?

代码

(由于我大量使用了 STL,所以效率并不太高 懒 呵呵

#include <bits/stdc++.h>

#define int long long

#define rr read()

using namespace std;

const int N = 4e5 + 10;

// 快读

inline int read()
{
    int num = 0, flag = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            flag = -1;
    for (; isdigit(ch); ch = getchar())
        num = (num << 3) + (num << 1) + ch - '0';
    return num * flag;
}

// 存放元素

struct node
{
    int x, y;
    node()
    {
        x = y = 0;
    }
    node(int _x, int _y)
    {
        x = _x, y = _y;
    }
    bool operator<(const node &t) const
    {
        return x < t.x;
    }
};

vector<node> q;
vector<node> a;

set<node> uq;
set<node> ad;

unordered_map<int, int> to;

// 树状数组

int s[N];

inline int lobit(int x)
{
    return x & -x;
}

void upd(int x, int t)
{
    for (; x < N; x += lobit(x))
        s[x] += t;
}

int que(int x)
{
    int res = 0;
    for (; x; x -= lobit(x))
        res += s[x];
    return res;
}

signed main()
{
    int m = rr;
    for (int i = 0; i < m; ++i)
        q.push_back({rr, rr}), uq.emplace(q[i].x, 1), uq.emplace(q[i].y, 1);

    // 去重和合并

    set<node>::iterator _a = uq.begin(), _b(_a);
    for (++_b; _b != uq.end(); ++_a, ++_b)
        if (_a->x < _b->x - 1)
            ad.emplace(_b->x - 1, _b->x - _a->x - 1);
    uq.insert(ad.begin(), ad.end());

    int n = 0;
    for (node i : uq)
        to[i.x] = n++, a.push_back({n, i.y});

    // 进行交换操作

    for (int i = 0; i < m; ++i)
        swap(a[to[q[i].x]], a[to[q[i].y]]);

    // 求逆序对

    int ans = 0;
    for (int i = n - 1; i >= 0; --i)
    {
        ans += que(a[i].x - 1) * a[i].y;
        upd(a[i].x, a[i].y);
    }

    printf("%lld\n", ans);
    return 0;
}
posted @ 2023-09-03 17:35  RainPPR  阅读(15)  评论(0编辑  收藏  举报