关于莫队算法的总结

莫队算法是用来骗分的……这个算法的使用前提是

在不强制在线的情况下,对于[l,r],[l',r']的区间询问,我们需要要O(|l-l'|+|r-r'|)次基本操作从[l,r]转移得到[l',r']的答案

可以发现这就是个高能暴力,只不过因为转移方向的优越带来比裸暴力更优的时空复杂度

如果说cdq分治是花费了复杂度干掉了在线,那么莫队就是花费复杂度干掉了区间

对于所有的询问,把当前询问包含的元素叫作当前的处理集合,然后暴力转移维护处理集合来解决下一个区间的询问

首先是最基本的:序列上应用莫队

现在流行的做法是分块,先分成sqrt(n)个块,我们把每个询问按照左端点所在块的编号作为第一关键字,右端点作为第二关键字排序

按这个顺序暴力转移,考虑两种情况

1. 相邻询问第一关键字相等等于x,这样由于第二关键字是递增的,所以所有第一关键字等于x的询问处理总的复杂度是O(n*转移所需复杂度)的,由于第一关键字最多sqrt(n)种取值

2. 第一关键字不等,那么相邻询问转移是O(n*转移所需复杂度)的,但是这样的情况最多出现O(sqrt(n))

所以可以证明复杂度=O(nsqrt(n)*转移所需复杂度)

简单例题:bzoj2038,bzoj3781由于太水我就不放代码了

例题:bzoj3809 比上面稍微难一点,由于颜色也有区间,所以我们可以考虑再对美丽度分块,这样就可以快速统计了,复杂度为O((m+n)sqrt(n))

下面是比较难的:树上莫队

树怎么分块?请见bzoj1086手把手教树分块,我是orz vfk的分块方式的

树上分块之后,我们就可以树上莫队了

但是等等,不对啊,树上的路径询问怎么转移?

这里我们引入一个东西叫做对称差,说白了就是异或(在处理集合的存在性取反)

我们设S(x,y)代表x,y之间路径的处理集合,h(x)代表节点x的元素

显然对于询问[x,y],处理集合S(x,y)=S(x,root) xor S(y,root) xor h(lca(x,y));

现在考虑转移到询问[x',y](先转移x,y类似),考虑lca只有一个元素,我们可以最后处理

定义T(x,y)=S(x,root) xor S(y,root)

则T(x',y)=S(x‘,root) xor S(y,root)=S(x',root) xor S(x,root) xor S(x,root) xor S(y,root)=T(x',x) xor T(x,y)

所以我们只要把x',x路径上的点存在性取反即可,然后就没了

这里排序有个小技巧,对于询问[x,y]把节点x所在块标号大于y的编号则交换x,y

简单例题:bzoj3757 太水了我就不放代码了

下面是最变态的了:树上带修改莫队

莫队是可以带修改了,学过cdq分治后不难想到把操作时间作为第三关键字排序,这样就搞定了

每次我们不仅要转移询问,还要让处理时间,还是要利用时间戳

注意这里出现了三个关键字,这里证明时间复杂度不难发现分块的大小是n^(2/3)最优,这样复杂度强行O(n^(5/3)),证明的话我就懒得写了

例题:bzoj3052 我很良心的放一个代码

  1 type way=record
  2        po,next:longint;
  3      end;
  4      node=record
  5        x,y,id:longint;
  6      end;
  7 
  8 var e:array[0..200010] of way;
  9     q,op:array[0..100010] of node;
 10     anc:array[0..100010,0..20] of longint;
 11     d,p,pre,last,prim,st,be,c,w,s,v:array[0..100010] of longint;
 12     f:array[0..100010] of boolean;
 13     ans:array[0..100010] of int64;
 14     test,size,i,j,n,m,x,y,ch,len,t,cur,t1,t0:longint;
 15     now:int64;
 16 procedure add(x,y:longint);
 17   begin
 18     inc(len);
 19     e[len].po:=y;
 20     e[len].next:=p[x];
 21     p[x]:=len;
 22   end;
 23 
 24 procedure dfs(x:longint);
 25   var i,y,h:longint;
 26   begin
 27     h:=t;
 28     i:=p[x];
 29     while i<>0 do
 30     begin
 31       y:=e[i].po;
 32       if y<>anc[x,0] then
 33       begin
 34         anc[y,0]:=x;
 35         d[y]:=d[x]+1;
 36         dfs(y);
 37         if t-h>=size then
 38         begin
 39           inc(len);
 40           while h<>t do
 41           begin
 42             be[st[t]]:=len;
 43             dec(t);
 44           end;
 45         end;
 46       end;
 47       i:=e[i].next;
 48     end;
 49     inc(t);
 50     st[t]:=x;
 51   end;
 52 
 53 procedure swap(var a,b:longint);
 54   var c:longint;
 55   begin
 56     c:=a;
 57     a:=b;
 58     b:=c;
 59   end;
 60 
 61 function cmp(a,b:node):boolean;
 62   begin
 63     if (be[a.x]=be[b.x]) and (be[a.y]=be[b.y]) then exit(a.id<b.id);
 64     if be[a.x]=be[b.x] then exit(be[a.y]<be[b.y]);
 65     exit(be[a.x]<be[b.x]);
 66   end;
 67 
 68 procedure sort(l,r:longint);
 69   var i,j:longint;
 70       x,y:node;
 71   begin
 72     i:=l;
 73     j:=r;
 74     x:=q[(l+r) shr 1];
 75     repeat
 76       while cmp(q[i],x) do inc(i);
 77       while cmp(x,q[j]) do dec(j);
 78       if not(i>j) then
 79       begin
 80         y:=q[i]; q[i]:=q[j]; q[j]:=y;
 81         inc(i);
 82         dec(j);
 83       end;
 84     until i>j;
 85     if l<j then sort(l,j);
 86     if i<r then sort(i,r);
 87   end;
 88 
 89 procedure del(x:longint);
 90   begin
 91     now:=now-int64(v[x])*int64(w[s[x]]);
 92     dec(s[x]);
 93   end;
 94 
 95 procedure ins(x:longint);
 96   begin
 97     inc(s[x]);
 98     now:=now+int64(v[x])*int64(w[s[x]]);
 99   end;
100 
101 procedure timec(i:longint);
102   begin
103     while (cur<t0) and (op[cur+1].id<q[i].id) do
104     begin
105       inc(cur);
106       if f[op[cur].x] then del(c[op[cur].x]);
107       c[op[cur].x]:=op[cur].y;
108       if f[op[cur].x] then ins(op[cur].y);
109     end;
110     while op[cur].id>q[i].id do
111     begin
112       if f[op[cur].x] then del(c[op[cur].x]);
113       if pre[cur]=0 then c[op[cur].x]:=prim[op[cur].x]
114       else c[op[cur].x]:=op[pre[cur]].y;
115       if f[op[cur].x] then ins(c[op[cur].x]);
116       dec(cur);
117     end;
118   end;
119 
120 procedure rev(x:longint);
121   begin
122     if not f[x] then ins(c[x])
123     else del(c[x]);
124     f[x]:=not f[x];
125   end;
126 
127 procedure work(x,y:longint);
128   begin
129     while x<>y do
130     begin
131       if d[x]>d[y] then
132       begin
133         rev(x);
134         x:=anc[x,0];
135       end
136       else begin
137         rev(y);
138         y:=anc[y,0];
139       end;
140     end;
141   end;
142 
143 function lca(x,y:longint):longint;
144   var i:longint;
145   begin
146     if x=y then exit(x);
147     if d[x]<d[y] then swap(x,y);
148     if d[x]>d[y] then
149     begin
150       for i:=trunc(ln(d[x])/ln(2)) downto 0 do
151         if d[x]-1 shl i>=d[y] then x:=anc[x,i];
152     end;
153     if x=y then exit(x);
154     for i:=trunc(ln(d[y])/ln(2)) downto 0 do
155       if anc[x,i]<>anc[y,i] then
156       begin
157         x:=anc[x,i];
158         y:=anc[y,i];
159       end;
160     exit(anc[x,0]);
161   end;
162 
163 procedure get(i:longint);
164   var z:longint;
165   begin
166     z:=lca(q[i].x,q[i].y);
167     rev(z);
168     ans[q[i].id]:=now;
169     rev(z);
170   end;
171 
172 begin
173   readln(n,m,test);
174   size:=0;
175   while size/n*size/n*size<1 do inc(size);
176   dec(size);
177   for i:=1 to m do
178     read(v[i]);
179   for i:=1 to n do
180     read(w[i]);
181   for i:=1 to n-1 do
182   begin
183     readln(x,y);
184     add(x,y);
185     add(y,x);
186   end;
187   len:=0;
188   dfs(1);
189   while t>0 do
190   begin
191     be[st[t]]:=len;
192     dec(t);
193   end;
194   for j:=1 to trunc(ln(n)/ln(2)) do
195     for i:=1 to n do
196     begin
197       x:=anc[i,j-1];
198       anc[i,j]:=anc[x,j-1];
199     end;
200   for i:=1 to n do
201   begin
202     read(c[i]);
203     prim[i]:=c[i];  //初始颜色
204   end;
205   for i:=1 to test do
206   begin
207     readln(ch,x,y);
208     if ch=0 then
209     begin
210       inc(t0);
211       op[t0].x:=x; op[t0].y:=y; op[t0].id:=i;
212       pre[t0]:=last[x];
213       last[x]:=t0;  //记录上一个关于点x的操作,方便解决时间倒流
214     end
215     else begin
216       inc(t1);
217       q[t1].x:=x; q[t1].y:=y; q[t1].id:=i;
218       if be[q[t1].x]>be[q[t1].y] then swap(q[t1].x,q[t1].y);
219     end;
220   end;
221   sort(1,t1);
222   timec(1);
223   work(q[1].x,q[1].y);
224   get(1);
225   for i:=2 to t1 do
226   begin
227     timec(i);
228     work(q[i-1].x,q[i].x);
229     work(q[i-1].y,q[i].y);
230     get(i);
231   end;
232   for i:=1 to test do
233     if ans[i]<>0 then writeln(ans[i]);
234 end.
View Code

总之莫队是一个有效的骗分神器,但是他太一般化了,所以一些特殊的问题无法做的更好,尽量不要一上来就写莫队

原文地址:https://www.cnblogs.com/phile/p/4473557.html