zoukankan      html  css  js  c++  java
  • 7.20关于莫队算法的一些初步理解

    这个算法说实话,我觉得并没有掌握到他的精髓,但是我觉得我还是理解了一下他最基本的一些用法,我发现这个算法还是非常有意思的。

    下面这道题是莫队算法的始祖:
    P1494 [国家集训队]小Z的袜子

    我觉得我一个人可能也没法把这个东西说得太清楚,所以说我引用一下网上大佬的东西:

    排序巧妙优化复杂度,带来NOIP前的最后一丝宁静。几个活蹦乱跳的指针的跳跃次数,决定着莫队算法的优劣……

    下面的内容全为转载

    进行区间询问[l,r],输出该区间内随机抽两次抽到相同颜色袜子的概率。

    ·分析:

    首先考虑对于一个长度为n区间内的答案如何求解。题目要求Ans使用最简分数表示:那么分母就是n*n(表示两两袜子之间的随机组合),分子是一个累加和,累加的内容是该区间内每种颜色i出现次数sum[i]的平方。

    将莫队算法抬上议程。莫队算法的思路是,离线情况下对所有的询问进行一个美妙的SORT(),然后两个指针l,r(本题是两个,其他的题可能会更多)不断以看似暴力的方式在区间内跳来跳去,最终输出答案。

    掌握一个思想基础:两个询问之间的状态跳转。如图,当前完成的询问的区间为[a,b],下一个询问的区间为[p,q],现在保存[a,b]区间内的每个颜色出现次数的sum[]数组已经准备好,[a,b]区间询问的答案Ans1已经准备好,怎样用这些条件求出[p,q]区间询问的Ans2?

    考虑指针向左或向右移动一个单位,我们要付出多大的代价才能维护sum[]和Ans(即使得sum[],Ans保存的是当前[l,r]的正确信息)。我们美妙地对图中l,r的向右移动一格进行分析:

    如图啦。l指针向右移动一个单位,所造成的后果就是:我们损失了一个绿色方块。那么怎样维护?美妙地,sum[绿色]减去1。那Ans如何维护?先看分母,分母从n2变成(n-1)2,分子中的其他颜色对应的部分是不会变的,绿色却从sum[绿色]2变成(sum[绿色]-1)2 ,为了方便计算我们可以直接向给Ans减去以前该颜色的答案贡献(即sum[绿色]2)再加上现在的答案贡献(即(sum[绿色]-1)2 )。同理,观赏下面的r指针移动,将是差不多的。

    ·如图r指针的移动带来的后果是,我们多了一个橙色方块。所以操作和上文相似,只不过是sum[橙色]++。

    ·回归正题地,我们美妙的发现,知道一个区间的信息,要求出旁边区间的信息(旁边区间指的是当前区间的一个指针通过加一减一得到的区间),竟只需要O(1)的时间。

    ·就算是这样,到这里为止的话莫队算法依旧无法焕发其光彩,原因是:如果我们以读入的顺序来枚举每个询问,每个询问到下一个询问时都用上述方法维护信息,那么在你脑海中会浮现出l,r跳来跳去的疯狂景象,疯狂之处在于最坏情况下时间复杂度为:O(n2)————如果要这样玩,那不如写一个暴力程序。

    ·“莫队算法巧妙地将询问离线排序,使得其复杂度无比美妙……”在一般做题时我们时常遇到使用排序来优化枚举时间消耗的例子。莫队的优化基于分块思想:对于两个询问,若在其l在同块,那么将其r作为排序关键字,若l不在同块,就将l作为关键字排序(这就是双关键字)。大米饼使用Be[i]数组表示i所属的块是谁。排序如:

    ·值得强调的是,我们是在对询问进行操作。

    ·时间复杂度分析(分类讨论思想):

    首先,枚举m个答案,就一个m了。设分块大小为unit。

    分类讨论:

    ①l的移动:若下一个询问与当前询问的l所在的块不同,那么只需要经过最多2unit步可以使得l成功到达目标.复杂度为:O(munit)

    ②r的移动:r只有在Be[l]相同时才会有序(其余时候还是疯狂地乱跳,你知道,一提到乱跳,那么每一次最坏就要跳n次!),Be[l]什么时候相同?在同一块里面l就Be[]相同。对于每一个块,排序执行了第二关键字:r。所以这里面的r是单调递增的,所以枚举完一个块,r最多移动n次。总共有n/unit个块:复杂度为:O(n*n/unit)

    总结:O(nunit+nn/unit)(n,m同级,就统一使用n)

    根据基本不等式得:当n为sqrt(n)时,得到莫队算法的真正复杂度:

    O(n*sqrt(n))

    ·代码上来了(莫队喜欢while):

    (下面是我参考一位大佬写的博客!)

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define LL long long int
    const int maxn=50003,maxm = maxn;
    struct Ask{
        int l,r,i;
    }asks[maxm];
    int n,m,A[maxn],block[maxn];
    int cnt[maxn],len[maxm];
    LL ans,res[maxm];
    int cmp(Ask a1,Ask a2){
        if(block[a1.l]!=block[a2.l])return block[a1.l]<block[a2.l];
        else return block[a1.r]<block[a2.r];
    }
    inline void add(int v){
        ans+=cnt[v];
        cnt[v]++;
    }
    inline void remove(int v){
        ans+=1-cnt[v];
        cnt[v]--;
    }
    void Mo(){
        int L=1,R=0;
        for(register int i=1;i<=m;++i){
            len[asks[i].i]=asks[i].r-asks[i].l+1;
            if(asks[i].l==asks[i].r)continue;
            while(asks[i].l<L)add(A[--L]);
            while(asks[i].l>L)remove(A[L++]);
            while(asks[i].r<R)remove(A[R--]);
            while(asks[i].r>R)add(A[++R]);
            res[asks[i].i]=ans;
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        int unit = sqrt(n);
        for(register int i=1;i<=n;++i){
            scanf("%d",&A[i]);
            block[i]=(i-1)/unit+1;
        }
        for(register int i=1;i<=m;++i){
            scanf("%d%d",&asks[i].l,&asks[i].r);
            asks[i].i=i;
        }
        sort(asks+1,asks+m+1,cmp);
        Mo();
        for(register int i=1;i<=m;++i){
            if(len[i]==1||res[i]==0){
                printf("0/1
    ");
                continue;
            }
            LL num = (LL)len[i]*((LL)len[i]-1)/2;
            LL gg = __gcd(res[i],num);
            res[i]/=gg;
            num/=gg;
            printf("%lld/%lld
    ",res[i],num);
        }
    }
    
  • 相关阅读:
    《Microsoft Sql server 2008 Internals》读书笔记第十一章DBCC Internals(2)
    《Microsoft Sql server 2008 Internals》读书笔记第十一章DBCC Internals(9)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(10)
    CKEditor在asp.net环境下的使用一例
    《Microsoft Sql server 2008 Internals》读书笔记第五章Table(7)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(11)
    千万数据的连续ID表,快速读取其中指定的某1000条数据?
    javascript中的float运算精度
    .Net与Java的互操作(.NET StockTrader微软官方示例应用程序)
    《Microsoft Sql server 2008 Internals》读书笔记第十一章DBCC Internals(6)
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13329406.html
Copyright © 2011-2022 走看看