zoukankan      html  css  js  c++  java
  • 【优雅的区间问题暴力】莫队算法

    对dalao口中可以(O(n^{frac{3}{2}}))区间内绝大部分无修改离线问题的莫队算法,一直处于“好骑”的状态,最近终于找到了学习的机会,其实感觉,这着实是一个优雅的暴力。
    莫队算法的大前提,是可以利用已知的[l,r]内的答案,直接得到[l,r+1],[l+1,r],[l-1,r],[l,r-1]上的答案。
    以下内容默认上述转移为O(1)的。
    在此基础上,显然的,作为两次询问[l,r],[l',r']之间的转移,时间花费为O(|l-l'|+|r-r'|)。
    我们将每次询问看做二维平面上的点,则所有询问间转移的花费就为在沿着这个平面上最小直线斯坦纳树做的花费,不管你会不会,反正我是不会这种做法。
    因此,我们有个良好的替代品,分块。
    将每个询问按照[l->pos,r]的顺序依次排序,l->pos指的是l属于的区块编号,按照排序后的顺序去做,显然是相对较优的做法之一。
    分析一下时间复杂度:
    考虑分块大小为x的情况:
    考虑左指针移动次数:
    若在块内移动,每次最多移动(O(x))次,最多q次,时间复杂度为(O(xq))
    若在块外移动,每次最多移动(O(n/x+x))次,最多从块1移动到块n/x,每次最多加上x次块内移动,时间复杂度为O(n).
    当q较大时,总复杂度可以近似看为(O(xq)).
    考虑右指针移动次数:
    若在块内移动,每次最多移动(O(x))次,最多q次,时间复杂度为(O(xq))
    考虑块外移动,最坏情况由n到1,总共经过n/x块,最坏时间复杂度为(O(nq/x))
    总时间复杂度视x不定,但当x取(n^{frac{1}{2}})时,最坏时间复杂度最稳定,稳定于(O(qn^{frac{1}{2}}))
    结合上述论述可以发现,为了保证右端点的移动的时间复杂度稳定,x取(O(n^{frac{1}{2}}))是最优的选择,故总时间复杂度为(O(qn^{frac{1}{2}}))
    当n与q为同数量级时,可近似看为(O(n^{frac{3}{2}}))
    接下来贴一下模板,模板题传送门

    #include <math.h>
    #include <stdio.h>
    #include <algorithm>
    #define R register
    #define MN 50005
    #define ll long long
    int n,m,cnt[MN],col[MN];ll ans[MN],sum;
    struct Query{
        int l,r,id,pos;
        inline bool operator <(Query &x)const{
            return pos<x.pos||(x.pos==pos&&r<x.r);
        }
    }query[MN];
    inline int read(){
        R int x; R char c; R bool f;
        for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
        for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
        return f?-x:x;
    }
    int main(){
        n=read(),m=read();read();
        R int size=(int)sqrt(n);
        for (R int i=1; i<=n; ++i) col[i]=read();
        for (R int i=1; i<=m; ++i){
            query[i].l=read(),query[i].r=read(),query[i].id=i;
            query[i].pos=(query[i].l-1)/size+1;
        }std::sort(query+1,query+m+1);
        for (R int i=1,l=1,r=0; i<=m; ++i){
            while (l>query[i].l) sum+=(++cnt[col[--l]]<<1)-1;
            while (r<query[i].r) sum+=(++cnt[col[++r]]<<1)-1;
            while (l<query[i].l) sum-=(--cnt[col[l++]]<<1)+1;
            while (r>query[i].r) sum-=(--cnt[col[r--]]<<1)+1;
            ans[query[i].id]=sum;
        }for (R int i=1; i<=m; ++i) printf("%lld
    ",ans[i]);
    }
    
  • 相关阅读:
    闭包函数与装饰器
    函数的嵌套,名称空间以及作用域
    函数知识点整理
    文件处理,光标
    ☆Django☆---表的设计 生命周期 分组 解析 路由层 视图层
    ☆Django☆---注册功能
    ☆Django☆---初步学习
    ☆Django☆---学习Django前的了解 wsgiref jinja2
    ☆javaSctipt☆---基础学习
    ☆前端☆---博客园作业
  • 原文地址:https://www.cnblogs.com/Melacau/p/modui.html
Copyright © 2011-2022 走看看