NOIP 2012 提高组 借教室(vijos 1782) 线段树85分打法


         题目自备,总览中已给链接    ————>         总览链接:http://www.cnblogs.com/qq359084415/p/3371855.html


              线段树很短,该程序53行,当然经过丧心病狂的压行可以压到40多行 —  —   并且在本题中线段树打法要开很大的数组,

        理论上要开4*n的空间才能保证不爆,时效又不高,不提倡,不过可以练手~~~~~~~~~~~~~~~~~~~~~~


        var

        tag,min:array[0..4000001] of longint;      //tag 是记录要下传的值,不多解释;min记录当前区间所有天中供应的最小值

        n,z,s,t,d,m:longint;                                //除 z 外都为题目给出,z为全局变量,记录当前分配的人

     <————————————————————————————————————————————————————————>

     |                                                注意:要开范围四倍的数组~其他没什么好说的                                                    |

     <————————————————————————————————————————————————————————>

        Function small(a,b:longint):longint;

        begin

            if a>b then small := b

                      else small := a;

        end;

     <————————————————————————————————————————————————————————>

      |                                            一个求两数最小值的函数,返还值为小的那个数                                                      |

     <————————————————————————————————————————————————————————>

        procedure build(o,l,r:longint);                           //建树,o为当前区间编号,l 为该区间左指针    ,r 为该区间右指针

        var m:longint;                                            
        begin
          if l=r then read(min[o]) else begin                   //按照递归的先后  ,会按顺序遍历1 ~ n ,  所以可以边建树边读取
            m:=( l + r ) shr 1;                                  
            build(o shl 1 ,l ,m);                                      //递归左区间
            build(o shl 1+1,m+1,r);                               //递归右区间
            min[o]:=small(min[o shl 1],min[o shl 1+1]);   //由左右两个子树
          end;
        end;

     <————————————————————————————————————————————————————————>

     |      一个、一般的、线段树建树、过程,并在、递归过程中、求出、各区间内、供应教室数、最少的那天、供应的教室数。      |

     <————————————————————————————————————————————————————————>

        Procedure pushdown(o:longint);                         //下传tag值

        begin
          inc( tag[o shl 1] , tag[o]);                                //tag值下传
          dec(min[o shl 1] , tag[o]);                                //将左儿子减去它应减去的tag值
          inc( tag[o shl 1+1] , tag[o]);                            //tag值下传
          dec(min[o shl 1+1] , tag[o]);                            //将右儿子减去它应减去的tag值
          tag[o]:=0;                                                      //记得清零该点的tag值
       end;

     <————————————————————————————————————————————————————————>

      |        将该编号的 tag 值下传的过程,将、它的、子区间、减去、该子区间、原本该减却暂时没减的 tag 值 (因为 某段区间    |

      |  减去、某个要供应的值, 它的子区间也应该减去);                                                                                           |

      |       在这,只将该值、传给它的两个子区间就可,没必要做到底,等要用到该子区间时再对该点减去所有应减去的值(即为它 |

      |  的tag值)                                                                                                                                             |

     <————————————————————————————————————————————————————————>

        procedure putout(o,l,r:longint);

        var m:longint;

        begin

          if (l >= s)and(r <= t) then begin           //该区间在决策区间内,进行决策

            dec(min[o],d);                                   //从减去中减去供应值

            inc (tag[o],d);                                    //标记该点

            if min[o] < 0 then begin                      //改点区间出现某天供不应求,通知该人,跳出程序

              writeln('-1');

              writeln(z);

              halt;

            end;

            exit;                                                  //因为该区间已分配,不必继续递归

          end;
          if tag[o]>0 then pushdown(o);               //做到该点时,若  其tag值不为空,代表 它的  两个儿子有 tag 值没减

                                                                        那么  由于下面要递归它的左右儿子,则  对该点进行 tag 值下传处理

         m:=(l + r) shr 1;
         if s <= m then putout(o shl 1 ,l ,m);        //如果左区间有被覆盖,则递归左区间
         if t > m then putout(o shl 1+1,m+1,r);    //如果右区间有被覆盖,则递归左区间
         min[o]:=small(min[o shl 1],min[o shl 1+1]);//从两个子节点对父节点的最小值更新
       end;

     <————————————————————————————————————————————————————————>

     |                     很平常的线段树打法,还是得说 (—>  .  —>) 时效不高,空间不优, 非常堪忧。。                                    |

     <————————————————————————————————————————————————————————>

       begin

         readln(n,m);

         build(1,1,n);

         for z:=1 to m do begin

           readln(d,s,t);

           putout(1,1,n);                                     //寻找决策区间

         end;

         writeln(0);
       end.


                               ~~~~~~~~~~ END~~~~~~~~~~~

原文地址:https://www.cnblogs.com/qq359084415/p/3372115.html