NOIP 2012 提高组 借教室(vijos 1782) 总览
附:题目链接 vijos 1782 地址: https://vijos.org/p/1782
本题的关键就是对区间值得修改,从而判定是否满足条件
一、如何快速的对区间值进行修改?
很容易想到处理该类问题的线段树打法
并且针对该题,线段树的数组表示的应为该区间所有天中能借教室数量的最小值,因为最小值够借出则该区间符合要求,
并且还要打上tag,作为下传的量;但是 这样的时间复杂度还是很高的。vijos上只能过85。
二、于是引出本题的正解:二分 + 差分;
I 、常规二分~~~~~~~~~~~~~~~~~~~~~~~~~
保证 第一个要通知的人 在 l 到 r 的区间内,
所以 r 要取 n + 1;(如果最后该人的位置是n + 1,则意味供应满足所有需求~)
1、 每次取mid = (l + r) shr 1
2、 开一个 need 数组 记录 差分数列(need[1]=a[1]-a[0] ( a[0]=0 ),need[2]=a[2]-a[1],need[3]=
a[3]-a[2]......need[n]=a[n]-a[n-1])
根据差分数列的性质,只要修改起始值和终点值就可以完成对一段区间的修改:
将第 i 个人的申请添入满足只要 inc( need[ s[ i ] ] , d[ i ])
因为这将影响后面所有值,
所以还要dec( need[ t[ i ] +1 ] , d[ i ]);(关于s,t,d数组是什么看题目链接);
2、 将前 mid 个添加进差分数列
再从第 2 个开始 令 need[ i ] := need[ i ] + need[ i - 1 ]; //即 将 差分 变为 原数组; 执行need[ i ]
时need [ i - 1 ]的值已经是a[ i - 1 ]
//则 need [ i ] + a[ i - 1 ] = a[ i ] - a[ i - 1 ] + a[ i - 1 ] = a[ i ];不断上传
3、 如果前 mid天 第 i 天的需求大于 r[ i ](该天供应) 则 r 变为 mid
小于等于 r[ i ](该天供应) 则 l 变为 mid+1
(之所以要+1是因为该天满足要求,不在考虑区间内~)
4、 警告!!need的数组做一次就要清零一次,因为它是一个累加的数组,并不是直接赋值的!!
5、 边界自己处理(根据l 到 r 区间表示的意思不同边界也不同);
后面的文章有AC程序,主要是vijos数据较弱 - =,别的地方测会 超时 几个点,
但 可以优化,优化后也能过~ 还有常规二分也有其他打法~这里都不再做赘述~
II、特殊二分~~~~~~~~~~~~~~~~~~~~~~~~
即针对特殊题目的不同于二分标准结构的打法,由于是针对,所以时效快了近一倍~(¥ 。¥)~啦啦啦~
分析:
常规二分之所以时效低主要是因为不断的清零need的数组的对一个点的多次无意义赋值:
比如 第一天 ,如果到最后 供过于求 即 从 l=1 ; r=n+1;
到 l=n+1 ; r=n+1;
一共做了 log ( 2 ,n ) 次,单单第一个点就重复添加进need数组log( 2 , n )次并且不断清零,相当
于把一个东西删掉,在写入,这是完全没必要的。
特殊二分过程:
记当前要处理的区间为 l ,r ,再增加一个指标 f 来判定要在当前区间内添加还是删除( f = 1 为 增加 否则 为 删除 )
如 开始 l = 1; r = n; f = 1;
同二分过程,取 mid = (l + r) shr 1
因为 f=1 则 将 l 至 r 这段区间 内 的 所有需求 添加进 need数组(need和常规二分的差分数组need一样)
一样从 1 扫到 n:
i:=1;
o:=0;
while (i <= n) and (need[ i ] + o <= offer[ i ]) do begin
o:=need[ i ]+o;
i:=i+1;
end;
和常规二分的不同,常规二分做完无论如何都要清零need数组,但在这用o来传递 a[i] 的值,need数组不变
如果 i >= n 那么说明 分配的区间 过大 ,也就是说 l 到 m 这段区间不能全部满足,
那么此时就执行 删除过程:
r 缩进为 mid
将 f 赋成 -1 代表:要、在 l 到 r 的区间内、删除、后某些数,直到满足要求
f = -1 删除过程:
取 mid = ( l + r ) shr 1
将 mid 到 r 区间的数删除 重复直到 不超过;
III、中心思想~~~~~~~~~~~~~~~~~~~~~~~~
从本质上说这种方法就是先取区间内前一半
||
\/
如果没满出来,那么重复执行从剩下的区间再取前一半
||
\/
如果满出来,那么从上一个取的区间内前一半的区间
去删除掉后一半区间的数,重复执行直到又没满出来,
再执行上一步
直到该区间没数为止(可能还剩下一个,标程会提到)
复杂度分析
从理论上来讲,大多数点只执行了 1 到 2 次(最多就添加一次,发现满出后被删除)
need数组也不用一次次清零,时效比常规快了很多,具有针对的二分20个点测下来
也能比优化后的二分快0.5秒左右~ ¥ ¥
详见标程~
总的来说,有三种打法:
线段树85分打法
优点:打起来容易,代码不长;
缺点:在本题中时效不如二分高,空间不如二分优;
综合:略微堪忧;
常规二分
优点:按普通二分结构可快速写出代码,边界好判断;
缺点:不具有针对性使得在时效上难以突出,重复多;
综合:较弱的数据比线段树略快,但大数据也会超时;
根据该题修改后的二分
优点:空间优,时效高;
缺点:比要在常规二分上修改,不同的打法边界考虑也有差;
综合:比上面两种好;
END~~~~~~~~~~~~~~~~~~~~~~~~~~~~