zoukankan      html  css  js  c++  java
  • 莫队算法

    莫队在知乎回答了一波,推荐了一篇博客,但原网址挂了。最后凭借搜狗的网页快照,我终于一睹这篇博客的真容。

    例题 (2010年国家集训队论文答辩)小K的袜子

    对于询问([L,R]),设其中颜色为(x,y,z...)的袜子的个数为(a,b,c...)

    那么答案即为(cfrac{cfrac{a(a-1)}{2}+cfrac{b(b-1)}{2}+cfrac{c(c-1)}{2}+...}{cfrac{(R-L+1)(R-L)}{2}})

    化简得:(cfrac{a^2+b^2+c^2+...x^2-(a+b+c+d+.....)}{(R-L+1)(R-L)})

    即:(cfrac{a^2+b^2+c^2+...x^2-(R-L+1)}{(R-L+1)(R-L)})

    所以这道题目的关键是求一个区间内每种颜色数目的平方和。

    对于一般区间维护类问题,一般用线段树,但是这题完全不知道线段树怎么做,搜索后得知是莫队算法。

    莫队算法可以一个可高效解决绝大多数离线+无修改+区间查询问题的算法。这类问题具体是指:如果知道([L,R])的答案时,可以在(O(mathrm{g}(n)))的时间下得到([L,R-1],)([L,R+1],)([L-1,R],)([L+1,R])的答案的话,就可以(O(nsqrt n · mathrm{g}(n)))的时间内求出所有查询。

    对于莫队算法我感觉就是暴力。由于预先知道所有的询问,因此可以合理的组织计算每个询问的顺序以此来降低复杂度。

    假设我们算完([L,R])的答案后现在要算([L',R'])的答案。由于可以在(O(1))的时间下得到([L,R-1],)([L,R+1],)([L-1,R],)([L+1,R])的答案,所以计算([L',R'])的答案耗时(|L-L'|+|R-R'|)。如果把询问([L,R])看做平面上的点(a(L,R)),询问([L',R'])看做点(b(L',R'))的话,那么时间开销就为两点的曼哈顿距离。

    因此,如果将每个询问看做平面上的一个点,按一定顺序计算每个值,那开销就为曼哈顿距离的和。要计算到每个点,路径至少会形成一棵树。所以问题就变成了求二维平面的最小曼哈顿距离生成树

    这样只要顺着树边计算一次就OK了,可以证明时间复杂度为(O(nsqrt{n}))(这个我不会证明),但是这种方法的编程复杂度稍微高了一点。

    有一个比较优雅的替代品:先对序列分块,然后对于所有询问按照(L)所在块的大小排序,如果一样再按照(R)排序,最后再计算。为什么这样计算就可以降低复杂度呢?

    一、(i)(i+1)在同一块内,(r)单调递增,所以(r)(O(n))的。由于有(sqrt{n})块,所以这一部分时间复杂度是(O(nsqrt{n}))
    二、(i)(i+1)跨越一块,(r)最多变化(n),由于有(sqrt{n})块,所以这一部分时间复杂度是(O(nsqrt{n}))
    三、(i)(i+1)在同一块内时,变化不超过(sqrt{n}),跨越一块也不会超过(sqrt{n}),不妨看作是(sqrt{n})。由于有n个数,所以时间复杂度是(O(nsqrt{n}))
    于是就变成了(O(nsqrt{n}))了。

    详细过程见代码:

    #include<algorithm>
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<math.h>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=50010;
    typedef long long ll;
    ll num[maxn],up[maxn],dw[maxn],ans,aa,bb,cc;
    int col[maxn],pos[maxn];
    struct qnode
    {
        int l,r,id;
    } qu[maxn];
    bool cmp(qnode a,qnode b)
    {
        if(pos[a.l]==pos[b.l])
            return a.r<b.r;
        return pos[a.l]<pos[b.l];
    }
    ll gcd(ll x,ll y)
    {
        ll tp;
        while(tp=x%y)
        {
            x=y;
            y=tp;
        }
        return y;
    }
    void update(int x,int d)
    {
        ans-=num[col[x]]*num[col[x]];
        num[col[x]]+=d;
        ans+=num[col[x]]*num[col[x]];
    }
    int main()
    {
        int n,m,i,j,bk,pl,pr,id;
    
        freopen("in.txt","r",stdin);
        while(~scanf("%d%d",&n,&m))
        {
            memset(num,0,sizeof num);
            bk=ceil(sqrt(1.0*n));
            for(i=1;i<=n;i++)
            {
                scanf("%d",&col[i]);
                pos[i]=(i-1)/bk;
            }
            for(i=0;i<m;i++)
            {
                scanf("%d%d",&qu[i].l,&qu[i].r);
                qu[i].id=i;
            }
            sort(qu,qu+m,cmp);
            pl=1,pr=0;
            ans=0;
            for(i=0;i<m;i++)
            {
                id=qu[i].id;
                if(qu[i].l==qu[i].r)
                {
                    up[id]=0,dw[id]=1;
                    continue;
                }
                if(pr<qu[i].r)
                {
                    for(j=pr+1;j<=qu[i].r;j++)
                        update(j,1);
                }
                else
                {
                    for(j=pr;j>qu[i].r;j--)
                        update(j,-1);
                }
                pr=qu[i].r;
                if(pl<qu[i].l)
                {
                    for(j=pl;j<qu[i].l;j++)
                        update(j,-1);
                }
                else
                {
                    for(j=pl-1;j>=qu[i].l;j--)
                        update(j,1);
                }
                pl=qu[i].l;
                aa=ans-qu[i].r+qu[i].l-1;
                bb=(ll)(qu[i].r-qu[i].l+1)*(qu[i].r-qu[i].l);
                cc=gcd(aa,bb);
                aa/=cc,bb/=cc;
                up[id]=aa,dw[id]=bb;
            }
            for(i=0;i<m;i++)
                printf("%I64d/%I64d
    ",up[i],dw[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    04 UUID
    MD5加密算法(信息摘要算法)、Base64算法
    03 MD5加密、Base64处理
    MVC分层思想、SSM编程架构
    1网络编程基本概念
    Tomcat闪退的解决办法
    win10下的jdk1.8安装
    枚举练习
    1000元买物品分配
    win10解决vc++6.0不兼容问题方法
  • 原文地址:https://www.cnblogs.com/P6174/p/7723856.html
Copyright © 2011-2022 走看看