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;
}

浙公网安备 33010602011771号