zoukankan      html  css  js  c++  java
  • [学习笔记] 关于差分

    本篇参考 【顾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——我保证这是最后一次了【滑稽】)

  • 相关阅读:
    控件列[Coolite]上传文件
    阿里巴巴淘宝马云的SNS之梦有了新目标 收购“微博”
    十进制数真值配套自测连载(一)
    谷歌用户Siri当心!谷歌正从苹果业务中分走一杯羹
    服务协议Rest风格的web服务开发入门篇1
    注意测试数据hdu 1010 (DFS+剪枝)
    团队软件成为技术领导者研发团队管理经验分享
    字段数据用一句sql语句更新两个表并可更新对应的字段的值
    日期条件SQL 截取日期作为条件
    取消内存Myeclipse 9.0优化方法汇总
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/9885499.html
Copyright © 2011-2022 走看看