bzoj 2038 莫队算法

莫队算法,具体的可以看10年莫涛的论文。

大题思路就是假设对于区间l,r我们有了一个答案,那么对于区间l,r+1,我们

可以暴力的转移一个答案,那么对于区间l1,r1和区间l2,r2,需要暴力处理

的部分就是|r1-r2|+|l1-l2|如果将l看成x,r看成r,得到的暴力部分就是manhattan距离

那么我们将所有的询问,构成一张二维图,可以从一个点转移到另一个点,且总manhattan距离

尽可能的小,所以可以建立一颗manhattan mst,这样的话就可以得到最优的转移,但是实际来说

搞定一个manhattan mst需要的时间不小,我们可以不要最优解,将询问按l分块,只需要做到在每个

块中尽可能的优就行了,所以每个块中可以根据r排序,然后搞就行了

经莫队证明,这个算法的复杂度上界大概是o(n^1.5)

/**************************************************************

    Problem: 2038

    User: BLADEVIL

    Language: Pascal

    Result: Accepted

    Time:3164 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.

 

你可能感兴趣的:(ZOJ)