Yuno loves sqrt technology II 解题报告

Yuno loves sqrt technology II 解题报告

题意

求区间逆序对数量,但是十分的卡时间(250 ms)和空间(31 MB)。

思路分析

一个基础的想法,将询问离线下来然后用莫队处理,这样可以做到 \(O(n\sqrt n \log n)\),显然不够优秀。

性能瓶颈在哪里?多次询问已经通过离线+莫队做到 \(O(n\sqrt n)\) 了,但是求逆序对时加入一个数是 \(O(\log n)\) 的,查询也是,同时查询次数是 \(O(n\sqrt n)\) 的,修改只有 \(O(n)\)。不妨考虑平衡规划一下:让修改和查询的复杂度趋于相同,即需要一种支持 \(O(1)\) 查询 \(O(\sqrt n)\) 修改的东西来维护逆序对。

考虑到逆序对的本质实际上就是一个二维数点问题,可以使用扫描线处理。但是正常扫描线使用线段树或树状数组实现也是 \(O(n\log n)\) 的。嗯,不妨用值域分块来维护这个扫描线,这样可以做到 \(O(\sqrt n)\) 修改和 \(O(1)\) 查询。

此时,我们发现我们既把询问离线下来了(莫队),同时也需要把莫队扩展过程中的增量离线,因为我们需要将这些信息用于扫描线计算。于是就得到了莫队二次离线。

这样就能够做到总时间复杂度为 \(O(n\sqrt n)\),空间复杂度 \(O(n \sqrt n)\)。

Code:

#include <stdio.h>

#include <algorithm>
#include <bitset>
#include <climits>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

#define DEBUG(u) (std::cerr << "### " << u << " ###\n");

using int64 = long long;
typedef std::pair<int, int> PII;

struct Query {
    int l, r, id;
};

// 值域分块维护扫描线的基础事件
struct Event {
    int l, r, id, opt, nxt;
};

// 按 key 将离线事件挂到扫描线对应的位置
void AddEvent(std::vector<Event>& event, std::vector<int>& head, int key, int l, int r, int id, int opt, int n) {
    if (key < 1 || key > n || l > r) {
        return;
    }
    int idx = static_cast<int>(event.size());
    event.push_back({l, r, id, opt, head[key]});
    head[key] = idx;
}

// 值域分块进行扫描线
class ValueBlocks {
   private:
    int U, len, total;
    std::vector<int> cnt, ec;

   public:
    explicit ValueBlocks(int u) : U(u), len(std::max(1, (int)sqrt(u))), total((u + len - 1) / len), cnt(u + 1), ec(total + 1) {}

    void Clear() {
        std::fill(cnt.begin(), cnt.end(), 0);
        std::fill(ec.begin(), ec.end(), 0);
    }

    void AddGreater(int v) {
        int b = (v - 1) / len + 1;
        int st = (b - 1) * len + 1;

        for (int i = st; i < v; i++) {
            cnt[i]++;
        }
        for (int i = 1; i < b; i++) {
            ec[i]++;
        }
    }
    void AddLess(int v) {
        int b = (v - 1) / len + 1;
        int ed = std::min(U, b * len);

        for (int i = v + 1; i <= ed; i++) {
            cnt[i]++;
        }
        for (int i = b + 1; i <= total; i++) {
            ec[i]++;
        }
    }

    int QueryGreater(int x) const {
        int b = (x - 1) / len + 1;
        return cnt[x] + ec[b];
    }
    int QueryLess(int x) const {
        int b = (x - 1) / len + 1;
        return cnt[x] + ec[b];
    }
};

// 值域树状数组
class FenwickTree {
   private:
    int N;
    std::vector<int64> Tree;

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

   public:
    explicit FenwickTree(int InN) : N(InN + 1), Tree(InN + 1) {}

    void Update(int u, int k) {
        while (u < N) {
            Tree[u] += k;
            u += lowbit(u);
        }
    }

    int64 Query(int u) {
        int64 res = 0;
        while (u) {
            res += Tree[u];
            u -= lowbit(u);
        }
        return res;
    }
};

void MAIN() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    std::vector<Query> query;
    for (int i = 1; i <= m; i++) {
        int l, r;
        std::cin >> l >> r;
        query.push_back({l, r, i});
    }

    std::vector<int> cpy = a;
    std::sort(cpy.begin() + 1, cpy.end());
    cpy.erase(std::unique(cpy.begin() + 1, cpy.end()), cpy.end());
    for (int i = 1; i <= n; i++) {
        a[i] = std::lower_bound(cpy.begin() + 1, cpy.end(), a[i]) - cpy.begin();
    }

    std::vector<int64> suf(n + 2), pref(n + 1);
    FenwickTree fwkt1(n), fwkt2(n);
    for (int i = 1; i <= n; i++) {
        pref[i] = (i - 1) - fwkt1.Query(a[i]);
        fwkt1.Update(a[i], 1);
    }
    for (int i = n; i >= 1; i--) {
        suf[i] = fwkt2.Query(a[i] - 1);
        fwkt2.Update(a[i], 1);
    }

    for (int i = 1; i <= n; i++) {
        pref[i] += pref[i - 1];
    }
    for (int i = n; i >= 1; i--) {
        suf[i] += suf[i + 1];
    }

    std::vector<int> block(n + 1);
    int len = std::max(1, n / (int)sqrt(m));
    for (int i = 1; i <= n; i++) {
        block[i] = (i - 1) / len + 1;
    }

    std::sort(query.begin(), query.end(), [&](const Query& A, const Query& B) {
        if (block[A.l] != block[B.l]) {
            return block[A.l] < block[B.l];
        }
        if (block[A.l] & 1) {
            return A.r < B.r;
        } else {
            return A.r > B.r;
        }
    });

    std::vector<int64> delta(m + 1), ans(m + 1);  // 相对于上一轮的增量,用于离线处理
    std::vector<int> headL(n + 1, -1), headR(n + 1, -1);
    std::vector<Event> eventL, eventR;
    eventL.reserve(m);
    eventR.reserve(m);

    int l = 1, r = 0;
    // 一次莫队,将差量离线到扫描线上
    for (int i = 0; i < m; i++) {
        if (r < query[i].r) {
            delta[query[i].id] += pref[query[i].r] - pref[r];
            AddEvent(eventL, headL, l - 1, r + 1, query[i].r, query[i].id, -1, n);
        } else if (r > query[i].r) {
            delta[query[i].id] -= pref[r] - pref[query[i].r];
            AddEvent(eventL, headL, l - 1, query[i].r + 1, r, query[i].id, +1, n);
        }

        if (l > query[i].l) {
            delta[query[i].id] += suf[query[i].l] - suf[l];
            AddEvent(eventR, headR, query[i].r + 1, query[i].l, l - 1, query[i].id, -1, n);
        } else if (l < query[i].l) {
            delta[query[i].id] -= suf[l] - suf[query[i].l];
            AddEvent(eventR, headR, query[i].r + 1, l, query[i].l - 1, query[i].id, +1, n);
        }

        l = query[i].l;
        r = query[i].r;
    }

    ValueBlocks vb(static_cast<int>(cpy.size()) - 1);

    // 第一次扫描:从左向右维护前缀
    // 事件统计:前缀中的值 > 目标位置的值

    for (int i = 1; i <= n; i++) {
        vb.AddGreater(a[i]);

        for (int e = headL[i]; e != -1; e = eventL[e].nxt) {
            const Event& ev = eventL[e];
            int64 tmp = 0;
            for (int j = ev.l; j <= ev.r; j++) {
                tmp += vb.QueryGreater(a[j]);
            }

            delta[ev.id] += (int64)ev.opt * tmp;
        }
    }

    // 第二次扫描:从右向左维护后缀
    // 事件统计:后缀中的值 < 目标位置的值
    vb.Clear();

    for (int i = n; i >= 1; i--) {
        vb.AddLess(a[i]);

        for (int e = headR[i]; e != -1; e = eventR[e].nxt) {
            const Event& ev = eventR[e];
            int64 tmp = 0;
            for (int j = ev.l; j <= ev.r; j++) {
                tmp += vb.QueryLess(a[j]);
            }

            delta[ev.id] += (int64)ev.opt * tmp;
        }
    }

    int64 cur = 0;
    for (const Query& q : query) {
        cur += delta[q.id];
        ans[q.id] = cur;
    }

    for (int i = 1; i <= m; i++) {
        std::cout << ans[i] << "\n";
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    MAIN();

    return 0;
}
posted @ 2026-10-26 12:00  LYZHaHa  阅读(5)  评论(0)    收藏  举报