又开坑
2015.2.15填坑开始
写在前面的废话
莫队是用于一些离线区间查询问题的,比如查询某个区间内某个值有多少个,这类问题用数据结构做十分麻烦(其实是蒟蒻不会)。在处理带修改的区间总和问题时可以通过分治大法来达到比较好的结果。而莫队,就是一种神奇的分治大法。
具体做法:
前人之述备已,下面给学习的链接。
链接:
一份不错的总结:http://blog.csdn.net/mlzmlz95/article/details/43644653
苹果树很好的题解:
http://www.cnblogs.com/zyfzyf/p/4250029.html
http://naginikaido.github.io/2015/01/09/bzoj3757-%E8%8B%B9%E6%9E%9C%E6%A0%91-%E6%A0%91%E4%B8%8A%E8%8E%AB%E9%98%9F/
糖果公园很好的题解
http://vfleaking.blog.163.com/blog/static/174807634201311011201627/
自己的一些废话
莫队其实就是先对区间进行分类,把左端分成一块一块,然后保证快内的右端点单调递增。然后按照这个顺序进行暴力转移(多退少补)。
而树上的莫队就是如何构造出这个块,亲身试验是如果直接用dfs序暴力会慢到要死,而vfk大大的神奇分发会快很多(虽然还是比c++慢)。
具体做法就是记录当前节点的儿子数,如果儿子数超过了块的大小就单独变成一块,剩下的几个儿子和孙子连同这个节点上到上一个节点的块中。
然后访问到某个点时存在性取反,最后答案要算上lca
function dfs(x:longint):longint; var size,i,too,j:longint; begin size:=0; inc(time); dfn[x]:=time; for i:=1 to 16 do begin j:=fa[fa[x,i-1],i-1]; if j>0 then fa[x,i]:=j else break; end; i:=first[x]; while i>0 do begin too:=edge[i].toward; if too<>fa[x,0] then begin deep[too]:=deep[x]+1; fa[too,0]:=x; inc(size,dfs(too)); if size>=long then begin inc(total); while size>0 do begin block[p[top]]:=total; dec(size); dec(top); end; end; end; i:=edge[i].next; end; inc(top); p[top]:=x; exit(size+1) end;
不带修改就双关键字排序,带修改就三关键字排序。
小Z的袜子
模板题
bzoj3781:小B的询问
模板题
type arr=record left,right,pl,num:longint; end; var tot,col,ans:array[0..600000]of longint; ask:array[0..600000]of arr; long,n,m,kk:longint; procedure qsort(l,r:longint); var i,j,mid1,mid2:longint; tmp:arr; begin mid1:=ask[(l+r)>>1].num; mid2:=ask[(l+r)>>1].right; i:=l; j:=r; repeat while (mid1>ask[i].num) or (mid1=ask[i].num) and (mid2>ask[i].right) do inc(i); while (mid1<ask[j].num) or (mid1=ask[j].num) and (mid2<ask[j].right) do dec(j); if i<=j then begin tmp:=ask[i]; ask[i]:=ask[j]; ask[j]:=tmp; inc(i); dec(j); end; until i>j; if i<r then qsort(i,r); if l<j then qsort(l,j); end; procedure into; var i,j,k:longint; begin readln(n,m,kk); for i:=1 to n do read(col[i]); long:=trunc(sqrt(m)); for i:=1 to m do begin readln(ask[i].left,ask[i].right); ask[i].num:=ask[i].left div long+1; ask[i].pl:=i; end; qsort(1,m); end; procedure work; var l,r,i,j,sum:longint; begin fillchar(tot,sizeof(tot),0); sum:=0; l:=1; r:=0; for i:=1 to m do begin for j:=r+1 to ask[i].right do begin sum:=sum+tot[col[j]]*2+1; inc(tot[col[j]]); end; for j:=r downto ask[i].right+1 do begin sum:=sum-tot[col[j]]*2+1; dec(tot[col[j]]); end; for j:=l-1 downto ask[i].left do begin sum:=sum+tot[col[j]]*2+1; inc(tot[col[j]]); end; for j:=l to ask[i].left-1 do begin sum:=sum-tot[col[j]]*2+1; dec(tot[col[j]]); end; ans[ask[i].pl]:=sum; l:=ask[i].left; r:=ask[i].right; end; for i:=1 to m do writeln(ans[i]); end; begin into; work; end.
bzoj3757: 苹果树
树上莫队模板题:树上就是状态取反,然后答案要算上lca再减掉lca
type arr1=record u,v,a,b,pl:longint; end; arr2=record toward,next:longint; end; const maxn=500000; var fa:array[0..maxn,0..18]of longint; edge:array[0..maxn]of arr2; ask:array[0..maxn]of arr1; ans,first,deep,dfn,num,p,fft,much,col:array[0..maxn]of longint; pow:array[0..18]of longint; chose:array[0..maxn]of boolean; esum,top,sum,time,n,m,long,total,root:longint; procedure addedge(i,j:longint); begin inc(esum); edge[esum].toward:=j; edge[esum].next:=first[i]; first[i]:=esum; inc(esum); edge[esum].toward:=i; edge[esum].next:=first[j]; first[j]:=esum; end; procedure swap(var x,y:longint); var i:longint; begin i:=x; x:=y; y:=i; end; function dfs(x:longint):longint; var size,i,too,j:longint; begin inc(time); dfn[x]:=time; for i:=1 to 15 do if deep[x]>=pow[i] then fa[x,i]:=fa[fa[x,i-1],i-1] else break; size:=0; i:=first[x]; while i>0 do begin too:=edge[i].toward; if too<>fa[x,0] then begin deep[too]:=deep[x]+1; fa[too,0]:=x; inc(size,dfs(too)); if size>=long then begin inc(total); while size>0 do begin num[p[top]]:=total; dec(top); dec(size); end; end; end; i:=edge[i].next; end; inc(top); p[top]:=x; exit(size+1) end; procedure qsort(l,r:longint); var i,j,mid1,mid2,k:longint; tmp:arr1; begin i:=l; j:=r; k:=random(r-l)+l; mid1:=num[ask[k].u]; mid2:=dfn[ask[k].v]; repeat while (num[ask[i].u]<mid1) or (num[ask[i].u]=mid1) and (dfn[ask[i].v]<mid2) do inc(i); while (num[ask[j].u]>mid1) or (num[ask[j].u]=mid1) and (dfn[ask[j].v]>mid2) do dec(j); if i<=j then begin tmp:=ask[i]; ask[i]:=ask[j]; ask[j]:=tmp; inc(i); dec(j); end; until i>j; if i<r then qsort(i,r); if l<j then qsort(l,j) end; function lca(x,y:longint):longint; var max,i,j:longint; begin if deep[x]<deep[y] then swap(x,y); i:=0; j:=deep[x]-deep[y]; while pow[i]<=j do begin if j and pow[i]>0 then x:=fa[x,i]; inc(i); end; if x=y then exit(x); for i:=15 downto 0 do if (fa[x,i]<>fa[y,i]) then begin x:=fa[x,i]; y:=fa[y,i]; end; exit(fa[x,0]) end; procedure change(x:longint); begin if not chose[x] then begin inc(much[col[x]]); if much[col[x]]=1 then inc(sum); end else begin dec(much[col[x]]); if much[col[x]]=0 then dec(sum); end; chose[x]:=not chose[x] end; procedure solve(x,y:longint); begin while x<>y do if deep[x]>deep[y] then begin change(x); x:=fa[x,0]; end else begin change(y); y:=fa[y,0]; end end; procedure into; var i,j,k:longint; begin pow[0]:=1; for i:=1 to 16 do pow[i]:=pow[i-1]<<1; readln(n,m); long:=trunc(sqrt(n*ln(n)/ln(2))); for i:=1 to n do read(col[i]); for i:=1 to n do begin readln(j,k); if j=0 then root:=k else if k=0 then root:=j else addedge(j,k); end; time:=0; deep[root]:=1; dfs(root); if top>0 then begin inc(total); for i:=1 to top do num[p[i]]:=total; end; for i:=1 to m do begin read(ask[i].u,ask[i].v,ask[i].a,ask[i].b); if dfn[ask[i].u]>dfn[ask[i].v] then swap(ask[i].u,ask[i].v); ask[i].pl:=i; end; qsort(1,m) end; procedure work; var i,j:longint; begin sum:=0; solve(ask[1].u,ask[1].v); j:=lca(ask[1].u,ask[1].v); change(j); ans[ask[1].pl]:=sum; if (much[ask[1].a]>0) and (much[ask[1].b]>0) and (ask[1].a<>ask[1].b) then dec(ans[ask[1].pl]); change(j); for i:=2 to m do begin solve(ask[i-1].u,ask[i].u); solve(ask[i-1].v,ask[i].v); j:=lca(ask[i].u,ask[i].v); change(j); ans[ask[i].pl]:=sum; if (much[ask[i].a]>0) and (much[ask[i].b]>0) and (ask[i].a<>ask[i].b) then dec(ans[ask[i].pl]); change(j); end; for i:=1 to m do writeln(ans[i]) end; Begin into; work; end.
bzoj3052: [wc2013]糖果公园
太神了直接跪vkf大神的题解吧!
type arr1=record toward,next:longint; end; arr2=record new,old,pl:longint; end; arr3=record u,v,pl,t:longint; end; const maxn=200020; var edge:array[0..maxn]of arr1; ask1:array[0..maxn]of arr2; ask2:array[0..maxn]of arr3; first,deep,belong,dfn,pre,value,w,col,much,block,p:array[0..maxn]of longint; ans:array[0..maxn]of int64; fa:array[0..maxn,0..16]of longint; chose:array[0..maxn]of boolean; tot1,tot2,esum,total,time,n,m,long,top:longint; sum:int64; procedure swap(var x,y:longint); var i:longint; begin i:=x; x:=y; y:=i; end; procedure addedge(j,k:longint); begin inc(esum); edge[esum].toward:=k; edge[esum].next:=first[j]; first[j]:=esum; end; function dfs(x:longint):longint; var size,i,too,j:longint; begin size:=0; inc(time); dfn[x]:=time; for i:=1 to 16 do begin j:=fa[fa[x,i-1],i-1]; if j>0 then fa[x,i]:=j else break; end; i:=first[x]; while i>0 do begin too:=edge[i].toward; if too<>fa[x,0] then begin deep[too]:=deep[x]+1; fa[too,0]:=x; inc(size,dfs(too)); if size>=long then begin inc(total); while size>0 do begin block[p[top]]:=total; dec(size); dec(top); end; end; end; i:=edge[i].next; end; inc(top); p[top]:=x; exit(size+1); end; function check(x,y:arr3):boolean; begin if block[x.u]<block[y.u] then exit(true); if block[x.u]>block[y.u] then exit(false); if block[x.v]<block[y.v] then exit(true); if block[x.v]>block[y.v] then exit(false); if x.t<y.t then exit(true); exit(false); end; procedure qsort(l,r:longint); var i,j:longint; mid,tmp:arr3; begin i:=l; j:=r; mid:=ask2[(l+r)>>1]; repeat while check(ask2[i],mid) do inc(i); while check(mid,ask2[j]) do dec(j); if i<=j then begin tmp:=ask2[i]; ask2[i]:=ask2[j]; ask2[j]:=tmp; inc(i); dec(j); end; until i>j; if i<r then qsort(i,r); if l<j then qsort(l,j); end; function lca(x,y:longint):longint; var i:longint; begin if deep[x]<deep[y] then swap(x,y); for i:=16 downto 0 do if deep[fa[x,i]]>=deep[y] then x:=fa[x,i]; if x=y then exit(x); for i:=16 downto 0 do if fa[x,i]<>fa[y,i] then begin x:=fa[x,i]; y:=fa[y,i]; end; exit(fa[x,0]); end; procedure reverse(x:longint); begin if chose[x] then begin sum:=sum-int64(w[much[col[x]]])*value[col[x]]; dec(much[col[x]]); end else begin inc(much[col[x]]); sum:=sum+int64(w[much[col[x]]])*value[col[x]]; end; chose[x]:=not chose[x]; end; procedure change(x,y:longint); begin if chose[x] then begin reverse(x); col[x]:=y; reverse(x); end else col[x]:=y; end; procedure solve(x,y:longint); begin while x<>y do if deep[x]>deep[y] then begin reverse(x); x:=fa[x,0]; end else begin reverse(y); y:=fa[y,0]; end; end; procedure into; var i,j,k,l,sumcol:longint; begin readln(n,sumcol,m); long:=trunc(exp(ln(n)*2/3)); esum:=0; for i:=1 to sumcol do read(value[i]); for i:=1 to n do read(w[i]); for i:=1 to n-1 do begin readln(j,k); addedge(j,k); addedge(k,j); end; deep[1]:=1; time:=0; if dfs(1)>0 then begin inc(total); for i:=1 to top do block[p[i]]:=total; end; for i:=1 to n do begin read(col[i]); pre[i]:=col[i]; end; tot1:=0; tot2:=0; for i:=1 to m do begin read(j); if j=0 then begin inc(tot1); readln(j,k); ask1[tot1].pl:=j; ask1[tot1].new:=k; ask1[tot1].old:=pre[j]; pre[j]:=k; end else begin inc(tot2); readln(j,k); if j>k then swap(j,k); ask2[tot2].u:=j; ask2[tot2].v:=k; ask2[tot2].pl:=tot2; ask2[tot2].t:=tot1; end; end; qsort(1,tot2); end; procedure work; var lastu,lastv,lastime,i,j,k:longint; begin lastu:=1; lastv:=1; lastime:=0; sum:=0; for i:=1 to tot2 do begin for j:=lastime+1 to ask2[i].t do change(ask1[j].pl,ask1[j].new); for j:=lastime downto ask2[i].t+1 do change(ask1[j].pl,ask1[j].old); solve(lastu,ask2[i].u); solve(lastv,ask2[i].v); k:=lca(ask2[i].u,ask2[i].v); lastu:=ask2[i].u; lastv:=ask2[i].v; lastime:=ask2[i].t; // writeln(ask2[i].pl,' ',lastu,' ',lastv,' ',lastime,' ',k,' ',sum); // writeln; reverse(k); ans[ask2[i].pl]:=sum; reverse(k); end; for i:=1 to tot2 do writeln(ans[i]); end; begin into; work; readln; readln; end.