zoukankan      html  css  js  c++  java
  • 莫队学习

    莫队算法 典型的离线算法,不支持修改。不能解决强制在线的问题。莫队算法优化的核心是分块和排序。将大小为 (n) 的序列分为 (sqrt{n}) 块,从 (1)(sqrt{n}) 编号,然后根据分块结果对查询区间进行排序。一种方法是把查询区间按照左端点所在块的序号排序,如果左端点所在块相同,再按右端点排序

    复杂度分析

    莫队算法的复杂度为 (O(nsqrt{n}))

    • 排序sort对查询区间进行排序,平均复杂度为 (O(nlog n))
    • 左指针移动 设平均每个块 (i) 中分布有 (x_i) 个左端点,莫队添加、删除操作的复杂度都为 (O(1)) 。指针跨越整块的时间复杂度为 (O(sqrt{n})) ,最坏情况需要跨越 (n) 次。所以总复杂度为 (O(nsqrt{n}))
    • 右指针移动 设平均每个块 (i) 中分布有 (x_i) 个左端点,由于左端点同块的区间右端点有序,那么对于这 (x_i)个区间,右端点最坏只需总共 (O(n)) 的时间跳,即最坏需跳完整个序列。总共 (sqrt{n}) 个块,总复杂度为 (O(nsqrt{n}))
    • 总复杂度(O(nsqrt{n}) + O(nsqrt{n})+O(nlog n)=O(nsqrt{n}))

    卡常技巧

    O2优化

    #pragma GCC optimize(2) 开O2优化比不开O2优化快 (4-5) 倍甚至更多。基本可以跑 (1e6) 的数据范围。

    奇偶性排序

    将查询区间的排序函数换为如下:

    int cmp(query a, query b) {
        return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
    }
    

    主要原理是右指针跳完奇数块往回跳时在同一个方向能顺路把偶数块跳完,然后跳完这个偶数块又能顺带把下一个奇数块跳完。

    移动指针的常数压缩

    指针移动的写法改为如下:

    while(l < ql) now -= !--cnt[a[l++]];
    while(l > ql) now += !cnt[a[--l]]++;
    while(r < qr) now += !cnt[a[++r]]++;
    while(r > qr) now -= !--cnt[a[r--]];
    

    题目

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 1000005;
    const int maxq = 200005;
    int a[maxn], belong[maxn], ans[maxn], cnt[maxn];
    int n, q, ql, qr, sz, num, now;
    struct Q{
        int l ,r, id;
    }query[maxq];
    
    int cmp(Q a, Q b){
        return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
    }
    int main()
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        sz = sqrt(n);
        num = ceil((double)n / sz);
        for(int i = 1; i <= num; i++){
            for(int j = (i - 1) * sz + 1; j <= i * sz; j++){
                belong[j] = i;
            }
        }
        scanf("%d", &q);
        for(int i = 1; i <= q; i++){
            scanf("%d%d", &query[i].l, &query[i].r);
            query[i].id = i;
        }
        sort(query + 1, query + q + 1, cmp);
        int l = 1, r = 0;
        int now = 0;
        for(int i = 1; i <= q; i++){
            ql = query[i].l;
            qr = query[i].r;
            while(l < ql) now -= !--cnt[a[l++]];
            while(l > ql) now += !cnt[a[--l]]++;
            while(r < qr) now += !cnt[a[++r]]++;
            while(r > qr) now -= !--cnt[a[r--]];
            ans[query[i].id] = now;
        }
        for(int i = 1; i <= q; i++){
            printf("%d
    ", ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    JAVA获取昨天、今天、明天等日期
    IDEA设置调用方法时提示方法上的注释
    Hibernate使用distinct返回不重复的数据,使用group by 进行分组
    SpringBoot 自定义注解
    tailwindcss 使用总结
    nodejs nvm 包管理
    macos NPM 全局安装解决方案
    git 遇到修改github密码导致本地push失败解决方案
    Jupyter 快捷方式设置
    Vue indent eslint缩进webstorm冲突解决
  • 原文地址:https://www.cnblogs.com/solvit/p/11352306.html
Copyright © 2011-2022 走看看