分治专题 点对问题 学习笔记
P1257 平面上的最接近点对
这是最基础的问题哦。
两种思路。
一:暴力
我们可以直观地发现两个最近的点x坐标距离应该也比较近。所以我们可以考虑对于每一个点扫描离其最近的10个左右的点。
但是出题人可能会专门卡。所以我们把坐标轴旋转一个角度(旋转所有的点就可以),最短距离不变。
旋转方法即使用复数运算。
\(r(acos\theta+bsin\theta)\times (cos\alpha+sin\alpha)=(a'+b'i)(cos\alpha+sin\alpha)\)
二:分治
先从中间某个点分为两半递归计算得到一个答案ans
然后考虑左边部分一个点和右边部分一个点。
拿出距离中间距离小于等于ans的所有点根据y坐标排序。直接暴力找即可。中间记得随时更新ans,及时去掉没有必要扫的点。最后可以证明复杂度在\(nlogn\)至\(nlog^2n\)左右
两种方法都要掌握。
CF459D Pashmak and Parmida's problem
首先我们可以预处理出需要的所有f,前缀和即可。
然后就是一个很naive的二维偏序问题。
用树状数组都可以解决了。
P4423 [BJWC2011]最小三角形
使用类似的分治方法,改为三重枚举,加上必要剪枝即可解决问题。
或者旋转一个角度(多旋转几次取最小值)。类似方法。
CF429D Tricky Function(fucktion)
显然g可以用前缀和计算,套进去之后可以得到
\(f(i,j)=(i-j)^2+(si-sj)^2\)
这不就是两点距离公式吗。
于是又转化成了上述问题。
CF383D Antimatter
莫名其妙的计数题。
看这个数据就可以考虑dp。
直接可以想到f(i,j)是以i结尾的值为j的区间的个数。
直接转移就可以了。
CF549F Yura and Developers
要减去最大值,我们可以考虑每一个值在那些区间里面是最大的。
然后我们可以发现每一次最大值可以把原区间分为两个不可能相交的小区间,满足分治的特性。这个地方利用单调栈计算即可。(四毛子树!)
先考虑如何合并答案。首先最大值会把区间分为两半。我们枚举小的那一半区间的每一个位置固定为一个端点,然后维护多的那一半里面有多少个位置是满足条件的
明显地,我们可以把问题转化为从中间点(不含)到 多的那一半中的某一个点这一段的和为x的有多少个位置。
可以发现这个可以变为一个前缀和。最终可以转化为\((mid,r]\)有多少个位置的前缀和为x。
利用主席树维护即可。
但是这里是离线的,我们可以用一种vector黑科技。开值域大小个vector,往某个下标为i的vector里面按顺序加入所有前缀和为i的点。
之后我们询问某一段\([l,r]\)含有多少个前缀和为x的位置的时候,可以直接二分得到答案。(第一个小于等于r所在位置-第一个大于等于l所在位置)
CF480E Parking Lot
这是蓝题????
由于加入障碍点会使答案减小,而且难以维护新的答案在哪里产生,我们可以考虑倒着算。
那就变成了减少障碍点。
显然,答案有两种情况,第一种是继承减少之前的答案,另一种是包括了刚刚变为普通点的障碍点的矩阵。
我们只需要利用悬线法算出初始答案,然后想办法维护新答案即可。
利用类似悬线法的方法,我们设down[i],up[i]为向下/上扩展的最长距离。
然后枚举被修改的这一行,新答案一定包含这一行中的某些位置。
由于需要正方形,我们可以用two pointers(尺取法)去枚举当前行所有可能满足条件的区间。
假设当前区间长度为x,如果有当前区间最大的r+最小的l -1>=x,则表明当前区间满足条件。
我们可以用两个单调队列维护这个最大r和最小l,因为two pointers的l和r指针都是单调递增的。
或者可以二分横长度check是否满足条件。
时间复杂度为\(O(n^2)\)或者\(O(n^2logn)\)
UVA1608 不无聊的序列 Non-boring sequences
非常经典的分治了。
首先这种排列问题我们通常可以记录左边和右边第一个出现同样数的位置在哪里。
如果整个大区间不无聊,必然有一个位置上的数只出现了一次。
然后包含这个数的所有区间都合法了。然后递归判断左右两块小的即可。
我们需要从两边中间扫,这样时间复杂度才是对的。
总之,这种分治题目需要多见识一下。其中有着许多构造的思想和常见trick。
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15225047.html