P4514 上帝造题的七分钟
分治套扫描线
不开 O2 可过。
前言
二维树状数组的模板,但是这篇题解不讲二维树状数组。
感谢 H_Kaguya 和 yllcm 指教扫描线相关问题。
前置知识:差分、线段树、树状数组、扫描线、分治。
线段树和扫描线
使用扫描线、线段树和差分离线解决静态问题:平面加之后平面求和。
扫描线的收益:问题降一维,维护一个一维线段树即可。
扫描线的代价:问题转为在线,所以扫描线只能用一次。
将修改和查询按照某个坐标轴差分成两个平面修改或平面查询(将矩形拆成两个等长对边),本文中扫描线全部按
为保证答案正确,差分时将询问中高度较小一边高度
使用两次扫描线,第一次扫描时直接执行修改,在查询时将贡献乘以该查询的
可以感性理解为第一次统计查询范围内“不完整”的矩形,第二次统计查询范围内“完整”的矩形并且去掉第一次扫描加入的多余贡献。
时间复杂度
Code:扫描线解决静态二维平面加平面求和。
写得这么多是因为网上没有多少文章讲这个,至少笔者学习的时候没有找到。
分治和常数优化
这道题并非静态问题,但分治可以做到化动为静,需要离线处理。
按照时间顺序划出分治线,处理分治线左侧修改操作对分治线右侧查询操作的贡献即可,扫描线的使用方法上文已经讲了。关于分治,具体解释和使用方法可以看 OI Wiki,需要注意的是不能重复统计贡献,而且要按时间顺序进行操作。
分治执行
注意在第二次扫描之后清空数据结构。
Code:朴素代码,询问和修改使用了同样的结构体。评测记录,吸氧可过。
但是如果要吸氧才能通过,这篇文章就没有意义了,为什么不写正解呢?我自己推不出来正解的式子。
开始优化,注意到三个能够优化的细节:分治过程中多次对同一操作差分、分治过程中重新排序、线段树需要开 long long。
常数瓶颈应该在排序。考虑使用归并优化。分治线左边的操作无论如何都比右边的查询更早执行,可以先递归左半边和右半边,扫描线,归并左右区间。
然而操作差分之后才能排序。
把操作差分之后再分治不就行了?很遗憾,扫描线的过程中,同一个查询或修改操作差分后不能分开。
类似 FFT,分治总长度为
另外只有区间内同时存在修改和查询时才会影响答案,这可以用前缀和判断。
Code:此时的代码已经能通过六个测试点。
最后的瓶颈是线段树,改为区间加区间求和的树状数组之后可以通过题目,树状数组可以不开 long long 从而获得更小的常数。
AC Code:评测记录。
一个未使用的优化:由于补齐了序列,可以将分治改为倍增。
总结
效率比正解差,也许读者会认为这种解法不是很值得学习,但是它也有二维树状数组没有的优点。
- 统计贡献不受
y 轴值域影响:这一维被扫描线扫掉了,数据结构不需要关心它,它的值域也不影响扫描线进行。 - 统计贡献不受
x 轴值域影响:使用值域离散线段树记录区间,区间加时将加值乘以区间的实际长度,树状数组不能实现。
本题虽然是针对二维树状数组设计的,但分治套扫描线的解法也适用,并且这种离线分治的方法在更大的数据范围下表现会更好。
闲话
当初写这种解法的时候,没有打算通过,一步步优化后却取得了意料之外的成果,十分令人欣喜。
题解区没有这种做法,也许是认为效率太差?笔者认为能够不开 O2 优化通过已经足够,毕竟题目的数据范围较小,操作较多,树状数组以外的做法很难胜任。
虽然思路早就有了,但实现花费了很长时间,积累了宝贵的经验。