• 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~~~~~~~~~~~

  • 相关阅读:
    [资料]PHP中的__autoload
    [转]php 5.3新增的闭包语法介绍function() use() {}
    [资料]PHP中的ReflectionClass
    [资料]PHP中的命名空间
    Mysql Event
    PHP转换成对像
    [转]Win7自带便签怎么恢复内容
    [转]Windows7便笺妙用
    [转]ASP.NET下MVC1.0>2.0>3.0>4.0
    PHP类动态属性问题
  • 原文地址:https://www.cnblogs.com/qq359084415/p/3372115.html
Copyright © 2020-2023  润新知