莫隊算法講解


  莫隊算法的大體思路就是暴力的轉移,盡量的減少轉移的時間。

  假設我們求出了區間[l1,r1]的答案,那么對於區間[l1,r1+1]我們可以o(1)的轉移,對於不同的詢問,我們將l當做橫坐標,r當做縱坐標,這樣建立的一張圖,求最小manhattan生成樹,需要轉移的時間是最少的。

  但是求一顆manhattan最小生成樹的時間已經比較長了,所以我們退而求其次。將n個詢問分成floor(sqrt(n))個塊,對於每個塊我們按照r排序,然后每個塊之間暴力的轉移,經莫濤證明,時間復雜度是O(n^1.5)的。理論上所有的區間詢問都可以用這種方法嘗試。

  經典的應用為bzoj 2038:http://61.187.179.132/JudgeOnline/problem.php?id=2038

/**************************************************************
    Problem: 2038
    User: BLADEVIL
    Language: Pascal
    Result: Accepted
    Time:3136 ms
    Memory:2572 kb
****************************************************************/
 
//By BLADEVIL
type
    rec                         =record
        l, r, w, s              :longint;
    end;
     
var
 
    n, m                        :longint;
    c, size                     :array[0..50010] of int64;
    len                         :longint;
    a                           :array[0..50010] of rec;
    now                         :longint;
    col, ans                    :array[0..50010] of int64;
    all, num                    :int64;
     
procedure swap(var a,b:longint);
var
    c                           :longint;
begin
    c:=a; a:=b; b:=c;
end;
 
procedure swap_rec(var a,b:rec);
var
    c                           :rec;
begin
    c:=a; a:=b; b:=c;
end;
 
function gcd(a,b:int64):int64;
begin
    if a<b then exit(gcd(b,a)) else
    if b=0 then exit(a) else exit(gcd(b,a mod b));
end;
 
procedure qs(low,high:longint);
var
    i, j, xx, yy                :longint;
begin
    i:=low; j:=high; xx:=a[(i+j) div 2].w;
    yy:=a[(i+j) div 2].r;
    while i<j do
    begin
        while (a[i].w<xx) or (a[i].w=xx) and (a[i].r<yy) do inc(i);
        while (a[j].w>xx) or (a[j].w=xx) and (a[j].r>yy) do dec(j);
        if i<=j then
        begin
            swap_rec(a[i],a[j]);
            inc(i); dec(j);
        end;
    end;
    if i<high then qs(i,high);
    if j>low then qs(low,j);
end;
     
procedure init;
var
    i                           :longint;
     
begin
    read(n,m);
    for i:=1 to n do read(c[i]);
    len:=trunc(sqrt(m));
    for i:=1 to m do
    begin
        read(a[i].l,a[i].r);
        if a[i].l>a[i].r then swap(a[i].l,a[i].r);
        size[i]:=a[i].r-a[i].l+1;
        a[i].w:=a[i].l div len+1;
        a[i].s:=i;
    end;
    qs(1,m);
end;
     
procedure main;
var
    i, j                        :longint;
begin
    i:=1;
    while i<=m do
    begin
        now:=a[i].w;
        fillchar(col,sizeof(col),0);
        for j:=a[i].l to a[i].r do
        begin
            ans[a[i].s]:=ans[a[i].s]+2*(col[c[j]]);
            col[c[j]]:=col[c[j]]+1;
        end;
        inc(i);
        while a[i].w=now do
        begin
            ans[a[i].s]:=ans[a[i-1].s];
            for j:=a[i-1].r+1 to a[i].r do
            begin
                ans[a[i].s]:=ans[a[i].s]+2*(col[c[j]]);
                col[c[j]]:=col[c[j]]+1;
            end;
            if a[i-1].l<a[i].l then
            begin
                for j:=a[i-1].l to a[i].l-1 do
                begin
                    col[c[j]]:=col[c[j]]-1;
                    ans[a[i].s]:=ans[a[i].s]-2*col[c[j]];
                end;
            end else
                for j:=a[i].l to a[i-1].l-1 do
                begin
                    ans[a[i].s]:=ans[a[i].s]+2*(col[c[j]]);
                    col[c[j]]:=col[c[j]]+1;
                end;
            inc(i);
            if i>m then break;
        end;
    end;
    for i:=1 to m do
    begin
        if size[i]=1 then all:=1 else all:=size[i]*(size[i]-1);
        num:=gcd(ans[i],all);
        writeln(ans[i] div num,'/',all div num);
    end;
end;
     
     
begin
    init;
    main;
end.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM