「Ynoi2019 模拟赛」Yuno loves sqrt technology II

二次离线莫队。

时限缩小了,不能用那个在线解法。

先想莫队朴素做法,很简单,在移动端点时计算与区间里数构成的逆序对数量并对应加减,树状数组维护,时间复杂度 \(O(N \sqrt{N} \log N)\)

很显然过不了啊,考虑怎么去掉 \(O(\log N)\)

离线下来的莫队查询又构成了新的 \(O(N \sqrt{N})\) 个查询,以右端点向右移动为例,设莫队中此时区间为 \([l, r]\),现在要变到 \([l, r + 1]\),则我们需要求 \([l, r]\)\(> a_r\) 的值的个数,转化为 \([1, r]\)\(> a_r\) 的值的个数减去 \([1, l - 1]\)\(> a_r\) 的值的个数,发现前者容易 \(O(N \log N)\) 预处理,后者为了能砍掉 \(O(\log N)\), 我们再次离线下来,在 \(l - 1\) 处查询 \(> a_r\) 的值的个数,然后从 \(1\)\(N\) 依次处理即可,共 \(O(N \sqrt{N})\) 次查询,\(O(N)\) 次修改,两者不平衡,所以用值域分块做到 \(O(\sqrt{N})\) 修改,\(O(1)\) 查询,时间复杂度 \(O(N \sqrt{N})\)

这是我们发现空间开不下,所以进一步观察到每次加入的查询都是依次查询一个区间内的数在 \([1, l - 1]\) 内大于他们的数的个数,所以这些查询可以用一个三元组 \((x, L, R)\) 表示要依次查询 \(a_L\)\(a_R\)\([1, x]\) 内大于他们的数的个数,这样的三元组是 \(O(Q)\) 的,离线下来做就做完了。

/*
address:https://www.luogu.com.cn/problem/P5047
AC 2026/8/28 16:30
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n, m, q;
struct query {
    int bel, l, r, id;
    bool operator < (const query& o)const {
        if (bel == o.bel) return bel & 1 ? r < o.r : r > o.r;
        return bel < o.bel;
    }
}qry[N];
struct FenwickTree {
#define lowbit(x) (x & -x)
    int c[N];
    inline void init() { fill(c + 1, c + m + 1, 0); }
    inline void change(int x, int k) { for (;x <= m;x += lowbit(x)) c[x] += k; }
    inline int query(int x) {
        int ret = 0;
        for (;x > 0;x -= lowbit(x)) ret += c[x];
        return ret;
    }
}BIT;
struct node {
    int l, r, id;
    bool op, f;
};
vector<node>vec[N];
LL ans[N], tmp[N];
LL pre_rev[N], suf_rev[N];
int cnt[N];
int a[N], disc[N];
int siz;
inline void read(int& x) {
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
}
inline void init() {
    for (int i = 1;i <= n;++i)
        BIT.change(a[i], 1),
        pre_rev[i] = i - BIT.query(a[i]);
    BIT.init();
    for (int i = n;i >= 1;--i)
        BIT.change(a[i], 1),
        suf_rev[i] = BIT.query(a[i] - 1);
    int l = 1, r = 0;
    for (int i = 1;i <= q;++i) {
        if (r < qry[i].r) {
            vec[l - 1].push_back({ r + 1, qry[i].r, i, false, false });
            while (r < qry[i].r) ans[i] += pre_rev[++r];
        }
        if (l > qry[i].l) {
            vec[r].push_back({ qry[i].l, l - 1, i, true, false });
            while (l > qry[i].l) ans[i] += suf_rev[--l];
        }
        if (r > qry[i].r) {
            vec[l - 1].push_back({ qry[i].r + 1, r, i, false, true });
            while (r > qry[i].r) ans[i] -= pre_rev[r--];
        }
        if (l < qry[i].l) {
            vec[r].push_back({ l, qry[i].l - 1, i, true, true });
            while (l < qry[i].l) ans[i] -= suf_rev[l++];
        }
    }
}
int blk;
int bel[N], L[N], R[N];
int sum[N], sum_blk[N];
inline void update(int x) {
    int p = bel[x];
    for (int i = x;i <= R[p];++i) ++sum[i];
    for (int i = p;i <= blk;++i) ++sum_blk[i];
}
inline int query(int x) { return sum_blk[bel[x] - 1] + sum[x]; }
inline void solve() {
    int siz = sqrt(m);
    blk = (m + siz - 1) / siz;
    for (int i = 1;i <= m;++i) bel[i] = (i + siz - 1) / siz;
    for (int i = 1;i <= blk;++i) L[i] = (i - 1) * siz + 1, R[i] = min(i * siz, m);
    for (int i = 1;i <= n;++i) {
        update(a[i]);
        for (auto [l, r, id, op, f] : vec[i]) {
            if (!op && !f)
                for (int j = l;j <= r;++j) ans[id] -= i - query(a[j]);
            else if (!op && f)
                for (int j = l;j <= r;++j) ans[id] += i - query(a[j]);
            else if (op && !f)
                for (int j = l;j <= r;++j) ans[id] -= cnt[a[j] - 1] - query(a[j] - 1);
            else
                for (int j = l;j <= r;++j) ans[id] += cnt[a[j] - 1] - query(a[j] - 1);
        }
    }
}
int main() {
    read(n), read(q);
    for (int i = 1;i <= n;++i) read(a[i]), disc[++m] = a[i];
    sort(disc + 1, disc + m + 1);
    m = unique(disc + 1, disc + m + 1) - disc - 1;
    for (int i = 1;i <= n;++i) ++cnt[a[i] = lower_bound(disc + 1, disc + m + 1, a[i]) - disc];
    for (int i = 1;i <= n;++i) cnt[i] += cnt[i - 1];
    siz = sqrt(n);
    for (int i = 1;i <= q;++i) {
        int l, r;read(l), read(r);
        qry[i] = { l / siz + 1, l, r, i };
    }
    sort(qry + 1, qry + q + 1);
    init();
    solve();
    for (int i = 1;i <= q;++i) ans[i] += ans[i - 1], tmp[i] = ans[i];
    for (int i = 1;i <= q;++i) ans[qry[i].id] = tmp[i];
    for (int i = 1;i <= q;++i) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2026-08-28 17:01  keysky  阅读(3)  评论(0)    收藏  举报