莫队

 又开坑

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;
View Code

不带修改就双关键字排序,带修改就三关键字排序。

 

小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.
View Code

 

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.
View Code

 

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.
View Code

 

posted @ 2015-02-09 22:01  Macaulish  阅读(239)  评论(0编辑  收藏  举报