您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页P4396 [AHOI2013] 作业(莫队+值域分块)

P4396 [AHOI2013] 作业(莫队+值域分块)

来源:华佗小知识
原题链接

题意

给定一个长度为 n n n ( n ≤ 1 e 5 n \leq 1e5 n1e5) 的序列 a n a_n an ( a i ≤ 1 e 5 a_i\leq 1e5 ai1e5), m m m ( m ≤ 1 e 5 m \leq 1e5 m1e5) 次询问,每次询问给定 l r a b,求区间 [ l , r ] [l,r] [l,r] 内大于等于 a a a 且小于等于 b b b 的数的个数,以及满足条件的不同数字的种数。

思路

容易想到莫队,但是对于区间的统计,如果直接莫队的话,需要使用树状数组 / 线段树等数据结构维护区间,时间复杂度会变成 O ( n m log ⁡ N ) O(n\sqrt m\log N) O(nm logN),显然无法接受。

朴素的想法,莫队的每次更新是 O ( 1 ) O(1) O(1) 的,查询是 O ( N ) O(N) O(N) 的。因为有 n m n\sqrt m nm 次更新操作和 m m m 次查询操作,显然更新比查询对复杂度的要求更苛刻。如果我们可以 O ( 1 ) O(1) O(1) 地更新、 O ( N ) O(\sqrt N) O(N ) 地查询,那么复杂度是可以接受的。

值域分块可以做到这一点。由于是单点更新,显然可以在 O ( 1 ) O(1) O(1) 的时间内完成对单点和块的更新,分块的查询是 O ( N ) O(\sqrt N) O(N ) 的,所以总复杂度为 O ( n m + m N ) O(n\sqrt m+m\sqrt N) O(nm +mN )

代码
#include <bits/stdc++.h>

constexpr int N = 100000;

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

    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<std::array<int, 5>> Q(m);
    for (int i = 0; i < m; ++i) {
        std::cin >> Q[i][0] >> Q[i][1] >> Q[i][2] >> Q[i][3];
        Q[i][4] = i;
    }

    int len = std::sqrt(n);
    std::sort(Q.begin(), Q.end(), [&](auto x, auto y) {
        if ((x[0] - 1) / len != (y[0] - 1) / len) return x[0] < y[0];
        if ((x[0] - 1) / len & 1) return x[1] > y[1];
        return x[1] < y[1];
    });

    int siz = std::sqrt(N);
    int num = (N + siz - 1) / siz;
    std::vector<int> st(num + 1), ed(num + 1), id(N + 1);
    for (int i = 1; i <= num; ++i) {
        st[i] = ed[i - 1] + 1;
        ed[i] = std::min(st[i] + siz - 1, N);
        for (int j = st[i]; j <= ed[i]; ++j) {
            id[j] = i;
        }
    }

    std::vector<std::pair<int, int>> ans(m);
    std::vector<int> cnt(N + 1), sum(num + 1), ap(num + 1);

    auto add = [&](int x) {
        cnt[a[x]]++;
        sum[id[a[x]]]++;
        if (cnt[a[x]] == 1) ap[id[a[x]]]++;
    };

    auto del = [&](int x) {
        cnt[a[x]]--;
        sum[id[a[x]]]--;
        if (cnt[a[x]] == 0) ap[id[a[x]]]--;
    };

    auto query = [&](int fr, int to) -> std::pair<int, int> {
        int res1 = 0, res2 = 0;
        for (int i = fr; i <= std::min(ed[id[fr]], to); ++i) {
            if (cnt[i]) res1 += cnt[i], res2++;
        }
        if (id[fr] != id[to]) {
            for (int i = std::max(fr, st[id[to]]); i <= to; ++i) {
                if (cnt[i]) res1 += cnt[i], res2++;
            }
        }
        for (int i = id[fr] + 1; i < id[to]; ++i) {
            res1 += sum[i];
            res2 += ap[i];
        }
        return {res1, res2};
    };

    int l = 1, r = 0;
    for (auto [ql, qr, fr, to, id] : Q) {
        while (l > ql) add(--l);
        while (r < qr) add(++r);
        while (l < ql) del(l++);
        while (r > qr) del(r--);
        ans[id] = query(fr, to);
    }

    for (auto [x, y] : ans) {
        std::cout << x << " " << y << "\n";
    }

    return 0;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务