您的位置:首页 > 其它

莫队算法讲解

2014-01-08 16:11 344 查看
  莫队算法的大体思路就是暴力的转移,尽量的减少转移的时间。

  假设我们求出了区间[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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: