• 线段树—Lazy_Tag


    转载LINK:LAZY_TAG

    先看一个具体问题吧 PKU 3468

    http://poj.org/problem?id=3468

    题意很清楚 1 ≤ N,Q ≤ 100000.

    "C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
    "Q a b" means querying the sum of AaAa+1, ... , Ab.

    用朴素的做法是O(NQ)的 明显TLE

    由于是区间统计问题 我们尝试用线段树解决

    先考虑线段树节点记录什么

    左右儿子 区间范围是必须的:ls[] rs[] l[] r[]

    为了回答询问我们还要记录一个S[]来保存当前区间的和

    *建树不是很难 注意要把s[]值从儿子处递推上来

    复制代码
     1 procedure build(a,b:longint);
    2  var x,mid:longint;
    3  begin
    4 inc(tt); x:=tt;
    5 l[x]:=a; r[x]:=b;
    6  if b-a=1
    7 thenbegin
    8 s[x]:=m[b];
    9 exit; end
    10 elsebegin
    11 mid:=(a+b)shr 1;
    12 ls[x]:=tt+1; build(a,mid);
    13 rs[x]:=tt+1; build(mid,b);
    14 s[x]:=s[ls[x]]+s[rs[x]];
    15 end;
    16  end;
    复制代码

    *当我们碰到C操作的时候把被a到b之间的区间覆盖所有线段树区间都修改一下

      +第三行判断是否被完全覆盖 是就直接修改s[]值

      +第五-七行判断是否有交集 递归修改

      +第八行 当且仅当没有被完全覆盖 才从儿子处得到新的s[]值

    复制代码
     1 procedure insert(x,a,b,c:longint);
    2  var mid:longint;
    3  if (a<=l[x])and(r[x]<=b)
    4 then s[x]:=s[x]+(r[x]-l[x])*c;
    5 mid:=(l[x]+r[x])shr 1;
    6  if a<mid then insert(ls[x],a,b,c);
    7  if mid<b then insert(rs[x],a,b,c);
    8  ifnot(a<=l[x])and(r[x]<=b)
    9 then s[x]:=s[ls[x]]+s[rs[x]];
    10  end;  
    复制代码

    *碰到Q操作的时候就递归查询所有在a到b区间内的子区间 求和得到答案

      -第五行如果被所查区间完全覆盖 直接返回值

      -第10-14行 根据交集情况递归查询

    复制代码
     1 function query(x,a,b:longint):int64;
    2  var mid:longint;
    3 ans:int64;
    4  begin
    5  if (a<=l[x])and(r[x]<=b)
    6 thenbegin
    7 query:=s[x];
    8 exit;
    9 end;
    10 ans:=0;
    11 mid:=(l[x]+r[x])shr 1;
    12  if a<mid then ans:=ans+query(ls[x],a,b);
    13  if mid<b then ans:=ans+query(rs[x],a,b);
    14 query:=ans;
    15  end;
    复制代码

    可以证明 查询操作的复杂度不超过O(Log2N)[常数小于2] 可以满足问题需求

    但是修改操作的最坏复杂度可以达到O(N) 而且常数比朴素还大 必须考虑优化

    我们可以通过一下两个示意图 看到两个操作的差距

    查询[2,10]

    修改[2,10]

    为了解决这个问题 著名的Lazy-Tag思想应运而生

    每次都把所有区间修改了太慢了 不如懒一点 先把帐记着 到时候再做

    懒-记帐=Lazy-Tag

    怎么记录呢? 我们需要再加一个域v[] 这个域就是标记

    用来记录当前节点为根的子树 是否需要统一加一个数 具体要加多少

    我们的程序就要改一下了

      +每当碰到要当前区间完全被欲修改区间覆盖时 直接给加在标记上 然后退出

      +每次访问到一个节点时 首先清空当前节点的标记

        -访问包括各种操作 不管是插入 删除 还是查询 甚至是仅仅用到节点的s[]值

        -清空不仅仅是用标记更新当前节点 还包括把标记下传给左右子树

    *我们用一个单独的过程clean来执行清空标记的操作

    需要注意的是 叶子节点不用下传标记 否则RE

    复制代码
     1 procedure clean(x:longint);
    2  begin
    3  if v[x]<>0
    4 thenbegin
    5 s[x]:=s[x]+(r[x]-l[x])*v[x];
    6 if ls[x]<>0then v[ls[x]]:=v[ls[x]]+v[x];
    7 if rs[x]<>0then v[rs[x]]:=v[rs[x]]+v[x];
    8 v[x]:=0;
    9 end;
    10  end;
    复制代码

    *修改后的修改过程

      +第一行行先清空标记

      +第五行如果完全覆盖 修改标记 退出

      +第10-12行 递归修改子树

      +第13-14行 更新当前区间的s[]值 必须先清空标记!

    复制代码
     1 procedure insert(x,a,b,c:longint);
    2  var mid:longint;
    3  begin
    4 clean(x);
    5  if (a<=l[x])and(r[x]<=b)
    6 thenbegin
    7 v[x]:=v[x]+c;
    8 exit;
    9 end;
    10 mid:=(l[x]+r[x])shr 1;
    11  if a<mid then insert(ls[x],a,b,c);
    12 if mid<b then insert(rs[x],a,b,c);
    13 clean(ls[x]); clean(rs[x]);
    14 s[x]:=s[ls[x]]+s[rs[x]];
    15 end;
    复制代码

    *修改后的查询

    也要加上清空标记

    而由于加上了这个递归完了更新s[]值也在所难免

    复制代码
     1 function query(x,a,b:longint):int64;
    2 var mid:longint;
    3 ans:int64;
    4 begin
    5 clean(x);
    6 if (a<=l[x])and(r[x]<=b)
    7 thenbegin
    8 query:=s[x];
    9 exit;
    10 end;
    11 ans:=0;
    12 mid:=(l[x]+r[x])shr 1;
    13 if a<mid then ans:=ans+query(ls[x],a,b);
    14 if mid<b then ans:=ans+query(rs[x],a,b);
    15 clean(ls[x]); clean(rs[x]);
    16 s[x]:=s[ls[x]]+s[rs[x]];
    17 query:=ans;
    18 end;
    复制代码

    完整的代码在文章最后

    当然Lazy-Tag可以用于极为强大的数据结构Splay

    解决的这个问题 我们还没得到运用Lazy-Tag的一般规律

    我还想到了另外一个问题 如果还带区间整体乘一个数呢?

    接下来讨论双标记的问题

    由于还要乘一个数 我们再加一个标记域

    用u[]表示节点x为根的子树需要加u[x]

    用v[]表示节点x为根的子树需要乘v[x]

    实际运算中 我们发现乘法和加法的操作序列是不确定

    所以我们要用2个标记来概括整个操作序列还需要考虑

    回过头去看上一个问题 只有加法的时候 无论加法序列是什么样子的

    根据加法的交换律和结合律 我们只要把整个序列的和保存下来就可以了

    但是有乘有加就不能简单的累记了

    比较好的方法是先规定标记的操作规则 先乘再加

    对于任意节点x v[x]和u[x]表示当前的s[x]需要更新为s[x]*v[x]+u[x]

    然后 每当我们要修改标记的时候

      如果要求给当前区间乘一个数c则将v[x]和u[x]都乘c

        (s[x]*v[x]+u[x])*c=s[x]*(v[x]*c)+(u[x]*c)

      如果要求给当前区间加一个数c 则只将u[x]+c即可

        (s[x]*v[x]+u[x])+c=s[x]*v[x]+(u[x]+c)

    只有当对于任意一种操作

    我们都保证能够无条件直接修改区间上的标记来达到效果

    我们才可以方便地使用Lazy-Tag

    否则当下传标记的时候还要考虑先清空儿子的标记

    clean过程就变成递归的过程 效率又变回O(N)了

    代码之需要在上一个问题上加以修改 贴在文章最后

    核心的过程是clean

    复制代码
     1 procedure clean(x:longint);
    2 begin
    3 if (u[x]<>0)or(v[x]<>1)
    4 thenbegin
    5 s[x]:=s[x]*v[x]+u[x]*(r[x]-l[x]);
    6 if ls[x]<>0
    7 thenbegin
    8 v[ls[x]]:=v[ls[x]]*v[x];
    9 u[ls[x]]:=u[ls[x]]*v[x]+u[x];
    10 end;
    11 if rs[x]<>0
    12 thenbegin
    13 v[rs[x]]:=v[rs[x]]*v[x];
    14 u[rs[x]]:=u[rs[x]]*v[x]+u[x];
    15 end;
    16 v[x]:=1; u[x]:=0;
    17 end;
    18 end;
    复制代码

    我们再讨论一个问题 也是双标记的

    维护一个序列

      支持操作给一个区间统一一个数

      和给一个区间统一修改成一个数

      还要询问区间和是多少

    很容易想到额外维护三个域 s[]{Sum} c[]{Cover} d[]{Delta}

    分别表示当前区间的和为s 当前区间被覆盖成c 当前区间需要加d

    同样的 分析两个标记域d[]和c[]的关系

    如果两个标记域都存在 那将会是一件棘手的事情 是先加再覆盖还是先覆盖再加呢?

    我们可以发现以下几个性质

      如果对某个区间x覆盖了c[x]之后 以前的c[x]和d[x]都会自动消失

      对某个区间加d[x]之后 如果c[x]有值 可以直接加在c[x]上

    这样我们就可以保证c[x]和d[x]最多只一个值存在 就没有上面的问题了

    所以不妨规定c[]的优先级高于d[] 当如果c[x]有值就只执行c[x] 否则执行d[x]

    我们又得到了一个结论 对于互相干涉的标记要定下明确的优先级 保证操作有序

    核心还是clean过程

    复制代码
     1 procedure down(x:longint);
    2 begin
    3 if c[x]<>key
    4 thenbegin
    5 s[x]:=c[x]*(r[x]-l[x]);
    6 if ls[x]<>0
    7 thenbegin
    8 c[ls[x]]:=c[x];
    9 d[ls[x]]:=0;
    10 end;
    11 if rs[x]<>0
    12 thenbegin
    13 c[rs[x]]:=c[x];
    14 d[rs[x]]:=0;
    15 end;
    16 c[x]:=key;
    17 end
    18 elseif d[x]<>0
    19 thenbegin
    20 s[x]:=s[x]+d[x]*(r[x]-l[x]);
    21 if ls[x]<>0thenif c[ls[x]]<>key
    22 then c[ls[x]]:=c[ls[x]]+d[x]
    23 else d[ls[x]]:=d[ls[x]]+d[x];
    24 if rs[x]<>0thenif c[rs[x]]<>key
    25 then c[rs[x]]:=c[rs[x]]+d[x]
    26 else d[rs[x]]:=d[rs[x]]+d[x];
    27 d[x]:=0;
    28 end;
    29 end;
    复制代码

    对Lazy-Tag的讨论就到这里

    下一篇讨论 线段树的扩展

    Bob HAN 原创 转载请注明出处 http://www.cnblogs.com/Booble/

     

    附 完整代码

    Simple

    复制代码
     1 const    maxn=200000;
    2  var l,r,ls,rs:array[1..maxn shl 1]of longint;
    3 v,s:array[1..maxn shl 1]of int64;
    4 m:array[1..maxn]of longint;
    5 n,tt,i,q,a,b,c:longint;
    6 ch,blank:char;
    7  procedure build(a,b:longint);
    8  var x,mid:longint;
    9  begin
    10 inc(tt); x:=tt;
    11 l[x]:=a; r[x]:=b;
    12 v[x]:=0;
    13  if b-a=1
    14 thenbegin
    15 s[x]:=m[b];
    16 exit; end
    17 elsebegin
    18 mid:=(a+b)shr 1;
    19 ls[x]:=tt+1; build(a,mid);
    20 rs[x]:=tt+1; build(mid,b);
    21 s[x]:=s[ls[x]]+s[rs[x]];
    22 end;
    23  end;
    24  procedure clean(x:longint);
    25  begin
    26  if v[x]<>0
    27 thenbegin
    28 s[x]:=s[x]+(r[x]-l[x])*v[x];
    29 if ls[x]<>0then v[ls[x]]:=v[ls[x]]+v[x];
    30 if rs[x]<>0then v[rs[x]]:=v[rs[x]]+v[x];
    31 v[x]:=0;
    32 end;
    33  end;
    34  procedure insert(x,a,b,c:longint);
    35  var mid:longint;
    36  begin
    37 clean(x);
    38  if (a<=l[x])and(r[x]<=b)
    39 thenbegin
    40 v[x]:=v[x]+c;
    41 exit;
    42 end;
    43 mid:=(l[x]+r[x])shr 1;
    44  if a<mid then insert(ls[x],a,b,c);
    45  if mid<b then insert(rs[x],a,b,c);
    46 clean(ls[x]); clean(rs[x]);
    47 s[x]:=s[ls[x]]+s[rs[x]];
    48  end;
    49  function query(x,a,b:longint):int64;
    50  var mid:longint;
    51 ans:int64;
    52  begin
    53 clean(x);
    54  if (a<=l[x])and(r[x]<=b)
    55 thenbegin
    56 query:=s[x];
    57 exit;
    58 end;
    59 ans:=0;
    60 mid:=(l[x]+r[x])shr 1;
    61  if a<mid then ans:=ans+query(ls[x],a,b);
    62  if mid<b then ans:=ans+query(rs[x],a,b);
    63 clean(ls[x]); clean(rs[x]);
    64 s[x]:=s[ls[x]]+s[rs[x]];
    65 query:=ans;
    66  end;
    67  begin
    68 assign(input,'simple.in'); reset(input);
    69 assign(output,'simple.out'); rewrite(output);
    70 readln(n,q);
    71 for i:=1to n do
    72 read(m[i]);
    73 tt:=0;
    74 build(0,n);
    75 readln;
    76 for i:=1to q do
    77 begin
    78 read(ch); read(blank);
    79 case ch of
    80 'Q': begin
    81 readln(a,b);
    82 writeln(query(1,a-1,b));
    83 end;
    84 'C': begin
    85 readln(a,b,c);
    86 insert(1,a-1,b,c);
    87 end;
    88 end;
    89 end;
    90 close(input); close(output);
    91 end.
    92
    复制代码

    SuperSImple

    复制代码
      1 const    maxn=100000;
    2 var l,r,ls,rs:array[1..maxn shl 1-1]of longint;
    3 u,v,s:array[1..maxn shl 1-1]of int64;
    4 m:array[1..maxn]of longint;
    5 n,q,tt,i,a,b,c:longint;
    6 ch,blank:char;
    7 procedure build(a,b:longint);
    8 var mid,x:longint;
    9 begin
    10 inc(tt); x:=tt;
    11 l[x]:=a; r[x]:=b;
    12 u[x]:=0; v[x]:=1;
    13 if b-a=1
    14 then s[x]:=m[b]
    15 elsebegin
    16 mid:=(a+b)shr 1;
    17 ls[x]:=tt+1; build(a,mid);
    18 rs[x]:=tt+1; build(mid,b);
    19 s[x]:=s[ls[x]]+s[rs[x]];
    20 end;
    21 end;
    22 procedure clean(x:longint);
    23 begin
    24 if (u[x]<>0)or(v[x]<>1)
    25 thenbegin
    26 s[x]:=s[x]*v[x]+u[x]*(r[x]-l[x]);
    27 if ls[x]<>0
    28 thenbegin
    29 v[ls[x]]:=v[ls[x]]*v[x];
    30 u[ls[x]]:=u[ls[x]]*v[x]+u[x];
    31 end;
    32 if rs[x]<>0
    33 thenbegin
    34 v[rs[x]]:=v[rs[x]]*v[x];
    35 u[rs[x]]:=u[rs[x]]*v[x]+u[x];
    36 end;
    37 v[x]:=1; u[x]:=0;
    38 end;
    39 end;
    40 procedure mult(x,a,b,c:longint);
    41 var mid:longint;
    42 begin
    43 clean(x);
    44 if (a<=l[x])and(r[x]<=b)
    45 thenbegin
    46 u[x]:=u[x]*c;
    47 v[x]:=v[x]*c;
    48 exit; end;
    49 mid:=(l[x]+r[x])shr 1;
    50 if a<mid then mult(ls[x],a,b,c);
    51 if mid<b then mult(rs[x],a,b,c);
    52 clean(ls[x]); clean(rs[x]);
    53 s[x]:=s[ls[x]]+s[rs[x]];
    54 end;
    55 procedure plus(x,a,b,c:longint);
    56 var mid:longint;
    57 begin
    58 clean(x);
    59 if (a<=l[x])and(r[x]<=b)
    60 thenbegin
    61 u[x]:=u[x]+c;
    62 exit; end;
    63 mid:=(l[x]+r[x])shr 1;
    64 if a<mid then plus(ls[x],a,b,c);
    65 if mid<b then plus(rs[x],a,b,c);
    66 clean(ls[x]); clean(rs[x]);
    67 s[x]:=s[ls[x]]+s[rs[x]];
    68 end;
    69 function query(x,a,b:longint):int64;
    70 var mid:longint;
    71 ans:int64;
    72 begin
    73 clean(x);
    74 if (a<=l[x])and(r[x]<=b)
    75 thenbegin
    76 query:=s[x];
    77 exit; end;
    78 ans:=0;
    79 mid:=(l[x]+r[x])shr 1;
    80 if a<mid then ans:=ans+query(ls[x],a,b);
    81 if mid<b then ans:=ans+query(rs[x],a,b);
    82 query:=ans;
    83 clean(ls[x]); clean(rs[x]);
    84 s[x]:=s[ls[x]]+s[rs[x]];
    85 end;
    86 begin
    87 assign(input,'ssimple.in'); reset(input);
    88 assign(output,'ssimple.out'); rewrite(output);
    89 readln(n,q);
    90 for i:=1to n do
    91 read(m[i]);
    92 readln;
    93 tt:=0;
    94 build(0,n);
    95 for i:=1to q do
    96 begin
    97 read(ch); read(blank);
    98 case ch of
    99 'Q': begin
    100 readln(a,b);
    101 writeln(query(1,a-1,b));
    102 end;
    103 'M': begin
    104 readln(a,b,c);
    105 mult(1,a-1,b,c);
    106 end;
    107 'P': begin
    108 readln(a,b,c);
    109 plus(1,a-1,b,c);
    110 end;
    111 end;
    112 end;
    113 close(input); close(output);
    114 end.
    115
    复制代码

    Paint

    复制代码
      1 const    maxn=100000;
    2 key=-219;
    3 max=maxn*2;
    4 var l,r,ls,rs,c,d,s:array[1..max]of longint;
    5 v:array[1..maxn]of longint;
    6 m,k,i,tt,x,y,z:longint;
    7 ch,ignore:char;
    8 procedure down(x:longint);
    9 begin
    10 if c[x]<>key
    11 thenbegin
    12 s[x]:=c[x]*(r[x]-l[x]);
    13 if ls[x]<>0
    14 thenbegin
    15 c[ls[x]]:=c[x];
    16 d[ls[x]]:=0;
    17 end;
    18 if rs[x]<>0
    19 thenbegin
    20 c[rs[x]]:=c[x];
    21 d[rs[x]]:=0;
    22 end;
    23 c[x]:=key;
    24 end
    25 elseif d[x]<>0
    26 thenbegin
    27 s[x]:=s[x]+d[x]*(r[x]-l[x]);
    28 if ls[x]<>0thenif c[ls[x]]<>key
    29 then c[ls[x]]:=c[ls[x]]+d[x]
    30 else d[ls[x]]:=d[ls[x]]+d[x];
    31 if rs[x]<>0thenif c[rs[x]]<>key
    32 then c[rs[x]]:=c[rs[x]]+d[x]
    33 else d[rs[x]]:=d[rs[x]]+d[x];
    34 d[x]:=0;
    35 end;
    36 end;
    37 procedure update(x:longint);
    38 begin
    39 down(ls[x]); down(rs[x]);
    40 s[x]:=s[ls[x]]+s[rs[x]];
    41 end;
    42 procedure build(a,b:longint);
    43 var x,mid:longint;
    44 begin
    45 inc(tt); x:=tt;
    46 l[x]:=a; r[x]:=b;
    47 d[x]:=0; c[x]:=key;
    48 if b-a=1
    49 then s[x]:=v[b]
    50 elsebegin
    51 mid:=(a+b)shr 1;
    52 ls[x]:=tt+1; build(a,mid);
    53 rs[x]:=tt+1; build(mid,b);
    54 s[x]:=s[ls[x]]+s[rs[x]];
    55 end;
    56 end;
    57 procedure cover(x,a,b,v:longint);
    58 var mid:longint;
    59 begin
    60 down(x);
    61 if (a<=l[x])and(r[x]<=b)
    62 thenbegin c[x]:=v; d[x]:=0; end
    63 elsebegin
    64 mid:=(l[x]+r[x])shr 1;
    65 if a<mid then cover(ls[x],a,b,v);
    66 if b>mid then cover(rs[x],a,b,v);
    67 update(x);
    68 end;
    69 end;
    70 procedure insert(x,a,b,v:longint);
    71 var mid:longint;
    72 begin
    73 down(x);
    74 if (a<=l[x])and(r[x]<=b)
    75 thenif c[x]<>key
    76 then c[x]:=c[x]+v
    77 else d[x]:=d[x]+v
    78 elsebegin
    79 mid:=(l[x]+r[x])shr 1;
    80 if a<mid then insert(ls[x],a,b,v);
    81 if b>mid then insert(rs[x],a,b,v);
    82 update(x);
    83 end;
    84 end;
    85 function sum(x,a,b:longint):longint;
    86 var mid:longint;
    87 begin
    88 down(x);
    89 if (a<=l[x])and(r[x]<=b)
    90 then sum:=s[x]
    91 elsebegin
    92 sum:=0;
    93 mid:=(l[x]+r[x])shr 1;
    94 if a<mid then sum:=sum+sum(ls[x],a,b);
    95 if b>mid then sum:=sum+sum(rs[x],a,b);
    96 update(x);
    97 end;
    98 end;
    99 begin
    100 assign(input,'paint.in'); reset(input);
    101 assign(output,'paint.out'); rewrite(output);
    102 readln(m,k);
    103 for i:=1to m do
    104 read(v[i]);
    105 readln;
    106 tt:=0;
    107 build(0,m);
    108 for i:=1to k do
    109 begin
    110 read(ch);
    111 repeat read(ignore); until ignore='';
    112 read(x,y);
    113 if ch<>'S'then read(z);
    114 readln;
    115 case ch of
    116 'C':cover(1,x-1,y,z);
    117 'A':insert(1,x-1,y,z);
    118 'S':writeln(sum(1,x-1,y));
    119 end;
    120 end;
    121 close(input); close(output);
    122 end.
    123
    复制代码
  • 相关阅读:
    jdk1.5线程知识列表
    linux 常用命令记录
    spring batch 编码问题
    maven
    统计文本中每个单词出现的次数
    生成优惠券,并将优惠券存入Mysql
    python 2.7版本解决TypeError: 'encoding' is an invalid keyword argument for this function
    CSRF verification failed. Request aborted. 表单提交方法为POST时的报错
    InsecureRequestWarning: Unverified HTTPS request is being made.解决方法
    SQL Server 打印九九乘法表
  • 原文地址:https://www.cnblogs.com/teilawll/p/3280478.html
Copyright © 2020-2023  润新知