从CF1676H2看逆序对的两种求法

Problem - 1676H2 - Codeforces

思路

原问题可以以直接转化成求 \(a_i >= a_j(i <j)\) 的数量。

归并排序

原理很直接,归并排序就是为了减少逆序对的数量,所以直接在操作的时候记录减少的数量即可

排序后的数组无逆序对,归并排序的合并操作中,每次后段首元素被作为当前最小值取出时,前段剩余元素个数之和即是合并操作减少的逆序对数量

但是实现比较麻烦

std::vector<int> a, temp;

int n;

i64 merge(int left, int mid, int right) {
    int i(left), j(mid), k(left);
    i64 count(0);
    while(i <= mid - 1 and j <= right) {
        if (a[i] < a[j]) {
            temp[k++] = a[i++];
        }
        else {//因为题目要求a[i] >= a[j]
            temp[k++] = a[j++];
            count += mid - i;//又因为合并操作已保证左区间有序,所以i之后到mid的a[i]都大于等于a[j]
        }
    }
    while(i <= mid - 1) 
        temp[k++] = a[i++];
    while(j <= right) 
        temp[k++] = a[j++];
    for (i = left; i <= right; i++) 
        a[i] = temp[i];
    return count;
}

i64 mergeSort(int left, int right) {
    int mid(0);
    i64 count(0);
    if (right > left) {
        mid = left + right >> 1;
        count = mergeSort(left, mid);
        count += mergeSort(mid + 1, right);
        count += merge(left, mid + 1, right);
    }
    return count;
}

i64 aInversion(int n) {
    temp.resize(n);
    return mergeSort(0, n - 1);
}

void solve() {
    std::cin >> n;
    a.resize(n);
    for (auto& x : a) std::cin >> x;
    std::cout << aInversion(n) << '\n';
}

树状数组or线段树

题目说了\(a_i \leq n\)

考虑用 \(a_i\) 来作为一段 \(01\) 前缀和中 \(1\) 的下标,即通过前缀和来计数

从后往前扫,每次先查询包括该下标到 \(1\)​ (因为要求 \(a_i >= a_j\)​)的前缀和,显然就是对于当前数字的逆序对个数

  • 树状数组
template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;
    
    Fenwick(int n_ = 0) {
        init(n_);
    }
    
    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }
    
    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }
    
    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }
};

void solve() {
    int n;
    std::cin >> n;
    Fenwick<int> fen(n);
    std::vector<int> a(n);
    for (auto& x : a) std::cin >> x, x--;
    i64 ans(0);
    for (int i = n - 1; i >= 0; i--) {
        ans += fen.sum(a[i] + 1);//这里是因为前面对数据做了--防越界处理,而该fenwick的实现中查询是右开的
        fen.add(a[i], 1);
    }
    std::cout << ans << '\n';
}
  • 线段树
template<class Info>
struct SegmentTree {
    int n;
    std::vector<Info> info;
    SegmentTree() : n(0) {}
    SegmentTree(int n_, Info v_ = Info()) {
        init(n_, v_);
    }
    template<class T>
    SegmentTree(std::vector<T> init_) {
        init(init_);
    }
    void init(int n_, Info v_ = Info()) {
        init(std::vector(n_, v_));
    }
    template<class T>
    void init(std::vector<T> init_) {
        n = init_.size();
        info.assign(4 << std::__lg(n), Info());
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                info[p] = init_[l];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        build(1, 0, n);
    }
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = info[p] + v;
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
    }
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
};

struct Info {
    i64 sum;
};

Info operator+(Info a, Info b) {
    a.sum += b.sum;
    return a;
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (auto& x : a) std::cin >> x, x--;
    i64 ans(0);
    SegmentTree<Info> T(n + 1);
    for (int i = n - 1; i >= 0; i--) {
        ans += T.rangeQuery(0, a[i] + 1).sum;//这里是因为前面对数据做了--防越界处理,而该segmentTree的实现中查询是左闭右开的
        T.modify(a[i], {1});
    }
    std::cout << ans << '\n';
}
posted @ 2024-03-07 13:39  加固文明幻景  阅读(15)  评论(0编辑  收藏  举报