满足无区间修改,可离线,可$O(1)$得到相邻区间的计数,即可$O(qlogq+n^{\frac{3}{2}})$求出q个查询$[l,r],1≤l≤r≤n$

算法流程

将长度为n的区间分成约$\sqrt n$个块,每块的大小最多为$\lfloor\sqrt n\rfloor$。确定每个查询的左端点所属块号,若第i个查询为$[l_i,r_i]$,$l_i$确定的块号$\frac{l_i}{\lfloor\sqrt n\rfloor}$

将q个查询进行排序,若左端点所属块号相同,则按照右端点升序排序;否则按照左端点所属块号升序排序。

初始双指针$l=1,r=0$。查询排序后,对于第i次查询$[l_i,r_i]$,将$l$移动到$l_i$,将$r$移动到$r_i$。此过程中维护区间计数。注意不能出现$l>r$的情况,所以代码实现中移动判断的顺序有讲究。

双指针移动的时间复杂度为$O(n \sqrt n)$

我们分别考虑左右指针的移动次数。

对于右指针,在区间左端点所属同一块的区间右端点移动时间复杂度为$O(n)$,共$O(\sqrt n)$分块,总时间复杂度为$O(n\sqrt n)$

若分块共计分成sz块,区间左端点分配到第i块的个数为$a_i$,对于第i块中的左端点由于是无序的,我们考虑最坏的移动情况,就是每次移动需要跨越整个块,这时候所需的时间复杂度为$O(a_i\sqrt n)$。所有块所需时间复杂度$O(\sum \limits_{i=1}^{sz} a_i \sqrt n) = O(n \sqrt n)$

q次区间查询,每次查询求区间内去重后的元素个数。

#include <bits/stdc++.h>
#define SINGLE_INPUT
#define ll long long
#define N 1000006
#define MOD 998244353
using namespace std;

int bsz;
int cnt[N], v[N], ans[N];
int cur = 0;
struct Node {
    int l, r, id;
    bool operator<(Node& o) {
        return l / bsz == o.l / bsz ? r < o.r : l / bsz < o.l / bsz;
    }
} a[N];

void add(int x) {
    if (++cnt[v[x]] == 1)
        cur++;
}

void sub(int x) {
    if (--cnt[v[x]] == 0)
        cur--;
}

void sol() {
    int n, q;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    cin >> q;
    for (int i = 0; i < q; i++) {
        cin >> a[i].l >> a[i].r;
        a[i].id = i;
    }
    bsz = sqrt(n);
    sort(a, a + q);
    int l = 1, r = 0;
    for (int i = 0; i < q; i++) {
        while (a[i].l < l)
            add(--l);
        while (a[i].r > r)
            add(++r);
        while (a[i].l > l)
            sub(l++);
        while (a[i].r < r)
            sub(r--);
        ans[a[i].id] = cur;
    }
    for (int i = 0; i < q; i++) {
        cout << ans[i] << "\\n";
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifndef SINGLE_INPUT
    int t;
    cin >> t;
    while (t--) {
        sol();
    }
#else
    sol();
#endif
    return 0;
}

区间最大频次

int bsz;
int feq[N], cnt[N], v[N], ans[N];
int mxf = 0;
struct Node {
    int l, r, id;
    bool operator<(Node& o) {
        return l / bsz == o.l / bsz ? r < o.r : l / bsz < o.l / bsz;
    }
} a[N];

void add(int x) {
    cnt[feq[v[x]]]--;
    feq[v[x]]++;
    cnt[feq[v[x]]]++;
    mxf = max(mxf, feq[v[x]]);
}

void sub(int x) {
    cnt[feq[v[x]]]--;
    feq[v[x]]--;
    cnt[feq[v[x]]]++;
    if (mxf == feq[v[x]] + 1 && cnt[feq[v[x]] + 1] == 0)
        mxf = feq[v[x]];
}

void sol() {
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    for (int i = 0; i < q; i++) {
        cin >> a[i].l >> a[i].r;
        a[i].id = i;
    }
    bsz = sqrt(n);
    sort(a, a + q);
    int l = 1, r = 0;
    for (int i = 0; i < q; i++) {
        while (a[i].l < l)
            add(--l);
        while (a[i].r > r)
            add(++r);
        while (a[i].l > l)
            sub(l++);
        while (a[i].r < r)
            sub(r--);
        ans[a[i].id] = mxf;
    }
    for (int i = 0; i < q; i++) {
        cout << ans[i] << "\\n";
    }
}