莫隊算法

機房的衆神犇都在搞這個東西,本SB也摻和一下下吧。php

莫隊算法可用於解決一類可離線且在獲得區間\([l,r]\)的答案後,能在\(O(1)\)\(O(\log_2{n})\)獲得區間\([l,r+1]\)\([l-1,r]\)的答案的問題算法

先看這樣一個問題:數組

給出n個數字,m次詢問,每次詢問在區間\([l_i,r_i]\)之間任選兩個數字相等的機率是多少。(n,q<=50000)(小z的襪子)數據結構

在區間\([l,r]\)中,這個機率是:
\[\frac{\sum_{i=1}^{v}C(2,f(i))}{C(2,r-l+1)}\] (v表示數字值,f(i)表示數字i在區間內出現的次數)post

因爲沒有加和性質,傳統的線段樹什麼的徹底派不上用場了呢!優化

考慮分子,由於\(C(2,x)=\frac{x^2-x}{2}\),因此分子=\(\frac{\sum_{i=1}^{v}f(i)^2-\sum_{i=1}^{v}f(i)}{2}\)
顯然 \(\sum_{i=1}^{v}f(i)=r-l+1\)spa

若得知區間\([l,r]\)的答案怎麼求區間\([l,r+1]\)的答案呢?仔細想一想。恩,有了。區間\([l,r+1]\)與區間\([l,r]\)相比只多了一個元素Z,這種改動是很小的,那麼前式中分子的值\(S=S_0-f(Z)^2+(f(Z)+1)^2-1=S_0+2*f(Z)\),同時++f(z),恩,\(O(1)\)的。這樣的話,在處理下一個詢問\([l_i,r_i]\)時,複雜度就是\(O(|r-r_i|+|l-l_i|)\)的。一樣的方法,也能夠在\(O(1)\)內求出\([l-1,r]\)\([l+1,r]\),\([l,r-1]\)。這樣的方法對於隨機數據表現是很好的,但也不難給出故意卡你的數據。.net

這時,就須要莫隊算法來撐腰了,這也是莫隊算法優化的精髓。code

注意到,每一個區間能夠抽象成平面中的點,每次轉移的花費都至關與從某點到另外一點的曼哈頓距離的長度。恩,因此呢?blog

因此咱們花費的即是這些平面中的點聯通的曼哈頓距離。平面點的曼哈頓最小生成樹!

對!但平面點的曼哈頓最小生成樹怎麼求呢?枚舉兩兩點鏈接\(O(n^2)\),毫無心義。其實平面點的曼哈頓最小生成樹有基於平面區域劃分的\(O(nlog_2n)\)的求法,但咱們有更簡潔的方法。對,分塊!

神犇曰:分塊是個好東西

確實,利用分塊,咱們能夠實現\(O(n\sqrt{n})\)的時間複雜度。雖然求解平面點的曼哈頓最小生成樹是\(O(nlog_2n)\)的,但根據莫隊論文中的證實,用到這裏時,仍然是\(O(n\sqrt{n})\),只不過常數小一些罷了。

分塊的作法:
\(x=\sqrt(n)\),以\([1,x],[x+1,2x],[2x+1,3x]...\)分塊
用pos數組維護端點i在第pos[i]塊中,而後就搞唄。

這樣搞:

1):排序,以左段點所在的塊爲第一關鍵字,以右端點爲第二關鍵字

2):從左往右處理詢問(離線)

3):不斷調整l,r的位置並同時修改

時間複雜度證實:

右端點移動:
首先咱們考慮一個塊裏面的轉移狀況
因爲一個塊裏面的詢問都按右端點排序
因此咱們右端點在一個塊裏面最多移動n次
\(O(\sqrt{n})\)個塊,那麼同一個塊內的右端點移動最多就是\(O(n\sqrt{n})\)
而後考慮從一個塊到另外一個塊致使的右端點變化
最壞狀況,右端點由n到1,那麼移動n次
\(O(\sqrt{n})\)個塊
那麼從一個塊到另外一個塊的事件只會發生\(O(\sqrt{n})\)次……
因此這種右端點移動的次數也是\(O(n\sqrt{n})\)
沒有別的事件致使右端點移動了
左端點移動:
同一個塊裏面,因爲左端點都在一個長度爲\(O(\sqrt{n})\)的區間裏面
因此在同一塊裏面移動一次,左端點最多變化\(O(\sqrt{n})\)
總共有n個詢問……
因此同一塊裏面的移動最多n次
那麼同一個塊裏面的左端點變化最可能是\(O(n\sqrt{n})\)
考慮跨越塊
每由第i個塊到第i+1個塊,左端點最壞加上\(O(\sqrt{n})\)
總共能加上\(O(\sqrt{n})\)
因此跨越塊致使的左端點移動是\(O(n)\)
綜上,分塊作法是\(O(n*\sqrt{n})\)

總結

莫隊算法在解決離線區間詢問幾乎是無敵的。
恩,幾乎只要能離線,用分塊的莫隊算法都能取得一個使人滿意的的解法。
因此就有不少擴展(解決線段樹等數據結構因爲須要區間加和性而不能解決的問題),如區間衆數,平均數什麼的。
恩。棒!

附:
[BZOJ]2038 小Z的襪子 分塊 莫隊算法

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

const int maxn = 50000 + 500;
typedef long long LL;

LL gcd(LL a,LL b)
{
    return (b==0)?a:gcd(b,a%b);
}

int pos[maxn];
int col[maxn];
int f[maxn];
int n,m;

struct Query
{
    int l,r,id;
    LL a,b;
    friend bool operator < (const Query &R,const Query &T)
    {
        return pos[R.l]<pos[T.l] || (pos[R.l]==pos[T.l] && R.r<T.r);
    }
    void modify()
    {
        LL k=gcd(a,b);
        a/=k,b/=k;
    }
}Q[maxn];
bool cmp_id(const Query &a,const Query &b)
{
    return a.id<b.id;
}

void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&col[i]);
    int limit=(int)sqrt((double)n+0.5);
    for(int i=1;i<=n;++i)
        pos[i]=(i-1)/limit+1;//左端點分塊
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    sort(Q+1,Q+m+1);
}

void modify(int p,LL &ans,int add)
{
    ans=ans+2*add*f[col[p]]+1;
    f[col[p]]+=add;
}

void solve()
{
    LL ans=0;
    int l=1,r=0;
    for(int i=1;i<=m;++i)
    {
        if(r<Q[i].r)
        {
            for(r=r+1;r<Q[i].r;++r)
                modify(r,ans,1);
            modify(r,ans,1);
        }
        if(Q[i].l<l)
        {
            for(l=l-1;Q[i].l<l;--l)
                modify(l,ans,1);
            modify(l,ans,1);
        }
        if(Q[i].r<r)
            for(;Q[i].r<r;--r)
                modify(r,ans,-1);
        if(l<Q[i].l)
            for(;l<Q[i].l;++l)
                modify(l,ans,-1);
        if(Q[i].l==Q[i].r)
        {
            Q[i].a=0,Q[i].b=1;
            continue;
        }
        Q[i].a=ans-(Q[i].r-Q[i].l+1),Q[i].b=(LL)(Q[i].r-Q[i].l+1)*(Q[i].r-Q[i].l);
        Q[i].modify();
    }
    sort(Q+1,Q+m+1,cmp_id);
    for(int i=1;i<=m;++i)
        printf("%lld/%lld\n",Q[i].a,Q[i].b);
}

int main()
{
    init();
    solve();

    return 0;
}

Refrence:
http://foreseeable97.logdown.com/posts/158522-233333

http://ydcydcy1.blog.163.com/blog/static/21608904020134411543898/

http://vawait.com/manhattanmst/

http://blog.csdn.net/huzecong/article/details/8576908

相關文章
相關標籤/搜索