[学习笔记] 关于差分

本篇参考 【顾z】dalao的讲解

差分数组:

现讲一下差分数组的定义吧。

a[i]=Σnj=1  b[j]。

几个栗子:

原数组1,3,5,2,4。

差分数组1,2,2,-3,2。

那么这个数组有什么优势呢?

如果我们要在原数组中修改一段区间的值,假如是给l到r这段区间加上x,那么它在差分数组中的体现就是:b[l]+x,b[r+1]-x。

也就是说:我们把区间修改变成了单点修改。

例题:AT2442(洛谷上直接搜就好),洛谷P3948,P3943。其中P3943的题解戳这里。(难度由低到高排序,在这里再次感谢顾z dalao)

树上差分:

树上差分这个东西一般被用来解决树上的路径计数问题。

点差分:

那么当我们要修改7到8这一条链上的值,给它每个点加上1,我们应该怎么做呢?

对于一颗树(描红的点是要加的):

先给大家说一种非常简单的差分数组理解方式吧:

cnt[u]表示给根到u结点的路径经过的所有点都加上cnt[u]。

我们只需要在差分数组的7号点加1,8号点加1,他们的lca 4号点-1,lca的父亲2号点-1,就行了。

为什么呢?

cnt[7]+1:我们把1,2,4,7四个节点加上了1。

cnt[8]+1:我们把1,2,4,6,8五个节点加上了1。

注意到此时由根(1)到lca的父亲(2)的路径上所有点都多加了两遍1,而lca被多加了一次,所以:

cnt[4]-1:我们把1,2,4三个点都减了1。

cnt[2]-1:我们把1,2这两点减了1。

对于查询,在dfs时把以它为根的子树中差分数组的值都累加起来就是它当前的值了(就相当于是把差分数组搬到树上了嘛)。

得出,点差分:

cnt[u]++      cnt[v]++      

cnt[lca]--     cnt[father[lca]]--

具体代码:

procedure getans(u,father:longint);
var
  i,v:longint;
begin
  g[u]:=cnt[u];
  i:=head[u];
  while i<>0 do
  begin     v:=vet[i];     if v<>father then     begin       getans(v,u);       g[u]:=g[u]+g[v];     end;     i:=next[i];   end;   if g[u]>ans then ans:=g[u]; end; begin   for i:=1 to m do   begin
    read(x,y);     z:
=lca(x,y);     inc(cnt[x]); inc(cnt[y]);     dec(cnt[z]); dec(cnt[f[z,0]]);   end;   getans(1,0); //答案统计再g数组里。

end.

 

边差分:

类似的,我们要给u到v这一条链上所有边都加1,也是差不多的。

还是那张图(描红的边是要加的):

我们把边压到点里(压到下面的那个点)。

我们只需要在差分数组的7号点加1,8号点加1,他们的lca 4号点-2就行了。

为什么呢?

cnt[7]+1:我们把1到2,2到4,4到7这三条边都加1。

cnt[8]+1:我们把1到2,2到4,4到6,6到8这四条都加1。

注意到此时根(1)到它们的lca(4)的路径上所有的边都被多加了2次,所以:

cnt[4]-2:我们把1到2,2到4这两条边都减2。

得出边差分:

cnt[u]++      cnt[v]++

cnt[lca]-2 

很简单,不是吗?

具体代码:

procedure getans(u,father:longint);
var
  i,v:longint;
begin
  g[u]:=cnt[u];
  i:=head[u];
  while i<>0 do
  begin
    v:=vet[i];
    if v<>father then
    begin
      getans(v,u);
      g[u]:=g[u]+g[v];
      if (g[v]=num)and(dist[i]>=k) then flag:=true;
    end;
    i:=next[i];
  end;
end;
begin

  for i:=1 to m do
  begin
    read(u[i],v[i]);
    grand[i]:=lca(u[i],v[i]);
    len[i]:=d[u[i]]+d[v[i]]-2*d[grand[i]];
    if len[i]>max then max:=len[i];
  end;   getans(1,0); //答案存再g数组里。 end;

 

例题:洛谷P3128,P3258,P2680。其中P2680的题解戳这里。(难度按由低到高排序,再次感谢顾z dalao——我保证这是最后一次了【滑稽】)

posted @ 2018-10-31 20:00  WR_Eternity  阅读(145)  评论(0编辑  收藏  举报