其实此题并不卡常,只是昨天有人说这题全是0/1操作,或许可以压位,然后我采纳这个idea,刷了下榜。
一开始我把朴素线段树给直接换成压位,因为复杂度太大,并不比常数正常的线段树快。这里是纯压位代码
之后根据今年NOI2017D1T1压位线段树的经验,我把纯压位改成了压位线段树,这是代码
为什么压位线段树的相对于普通线段树的优化力度,并没有压位相对于常规暴力那么大?这是因为在常规问题中,压位优化是从
O(qn)
变成
O(qnw)
,而线段树问题中,压位优化是从
O(qlogn)
变成
O(qlognw)
,即
O(q(logn−logw)
,优化力度从除法变减法。
而且压位需要特殊处理两端的情况,一定程度上增大了常数和代码复杂度,这就导致NOI2017D1T1事后我在UOJ上被卡常。
不过在这题中一定程度上可以减少特殊处理两端时的常数。
首先简要描述下单纯的压位线段树的具体流程:
1.在线段树上找到两端所在块的值;
2.在线段树上修改两端;
3.再对中间的块进行赋值;
注意到区间的两端在线段树相当于单点,这就使我们可以迭代实现操作1和2。(优化1)
又注意到连续的两个1,2操作对象是同一个元素,所以操作2时可以不pushdown(优化2)
再大胆猜测左右端都操作后,中间的块也不用pushdown(优化3)
优化1代码(未迭代实现操作2)
优化1和2代码(未迭代实现操作2)
优化1,2,3代码
上述是宏观层面的卡常,还没用到“有理有据的底层优化”
底层优化1:操作2是直接加上增量,而不是重新maintain->代码
底层优化2:操作1是不用if,而是用三元运算符->代码
底层优化3:操作1是判循环结束不用!=
,而是用^
->代码
底层优化4:操作1修改循环变量时从三元运算符变成,位移并或上一个布尔表达式->代码
之所以说是底层优化,是因为基础运算次数没有变化
最后,在uoj,洛谷,bzoj,loj上我都拿到rk1
总结一下,在uoj上,复杂度的优化效果为1986ms,代码层面的高级常数优化效果为547ms,底层优化的效果为189ms(可能是我常数优化的能力比较差?但可能不是所有题都适合常数优化)
说了这么多,好像所有优化全是在辅助数据结构上,似乎跟树剖以及初始化(I/O)没有一点关系?
upd:我又发现压位以后线段树的结点范围变小,可以用short存,但实测效果不好(可能没控制变量?欢迎大佬进行试验)
NOI2015软件包管理器,大力卡常记
最新推荐文章于 2023-01-13 11:18:52 发布