莫队算法(基础莫队)小结(也做markdown测试)

莫队

基础莫队

本质是通过排序优化了普通尺取法的时间复杂度。

考虑如果某一列询问的右端点是递增的,那么我们更新答案的时候,右指针只会从左往右移动,那么i指针的移动次数是O(n)的。

当然,我们不可能让左右端点都单调来做到总体O(n)

考虑对左端点进行分块。

莫队排序:
左端点按照分块的编号来排,如果分块编号不同的话编号较小的靠前,如果相同的话右端点小的在前。可以证明这样排完序的话时间复杂度可以做到O(nn)

这样我们把区间分成了n块,每块的长度都是n,在每一块内部,所有查询的右端点是递增的。

右指针:在每一块内部,右端点递增,所以右端点走的总数不会超过n(注意每一块内部放的是左端点,右端点是完全有可能超出这个块的范围的,因此这里为n),一共有n块,所以右端点总共走的次数不会超过nn

左指针:先考虑每一次询问:

  1. 左指针在块内部移动,块的长度是n,因此最多只会移动n次。

  2. 左指针在相邻两块之间移动,最坏是从第一个块的左端点移动到第二个块的右端点,因此最坏移动2n次。

因为有q次询问,所以1是qn,2是2n
因为一共有n个块,我们从前往后要跨过n1次,每次最多是2n,所以时间复杂度是2n

所以总时间复杂度为O(qn)

// 代码为统计一段区间上是否有不相同的数,没有输出yes
int n, q;
int a[N];
vector<array<int, 3>> v;
int cnt[210];
int ans[N];
int len;

int get(int x) {
    return x / len;
}

void adds(int x, int &res) {
    if (!cnt[x]) res ++;
    cnt[x] ++;
}

void del(int x, int &res) {
    cnt[x] --;
    if (!cnt[x]) res --;
}

void solve() {
    cin >> n >> q;
    len = max(1, (int)sqrt((double)n * n / q));
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        a[i] += 100;
    }
    for (int i = 1; i <= q; i ++) {
        int l, r;
        cin >> l >> r;
        v.push_back({i, l, r});
    }
    auto cmp = [&](array<int, 3> &a, array<int, 3> &b) {
        int i = get(a[1]), j = get(b[1]);
        if (i != j) return i < j;
        return a[2] < b[2];
    };
    sort(v.begin(), v.end(), cmp);
    // i是右指针,j是左指针
    for (int k = 0, i = 0, j = 1, res = 0; k < q; k ++) {
        int id = v[k][0], l = v[k][1], r = v[k][2];
        while (i < r) adds(a[++ i], res);
        while (i > r) del(a[i --], res);
        while (j < l) del(a[j ++], res);
        while (j > l) adds(a[-- j], res);
        ans[id] = res;
    }
    for (int i = 1; i <= q; i ++)
        if (ans[i] == 1) cout << "YES\n";
        else cout << "NO\n";
}
posted @   lightmon  阅读(15)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示