基本技巧(长期更新)
贪心
分治
二分
看到使最大值最小或者使最小值最大时都可以尝试上二分。
注意二分的问题要有单调性(在某一点之前都不满足,之后都满足)。
二分的\(n\)种写法
虽然令人难蚌,但二分有很多种写法,尽量都了解/掌握。
虽然说是很多种写法但还是就记录主流的吧QwQ
第一种:
个人认为最好写最好理解的一种
在单调递增序列\(a\)中查找第一个大于等于\(x\)的值的位置。
区间为\([l,r]\),最终答案为\(ans\)。
\\初始化l,r,并且ans=-1
while(l<=r){
int mid=l+r>>1;
if(a[mid]>=x) ans=mid,r=mid-1;//l=mid+1或者r=mid-1,根据题目和自己写的check判断。
else l=mid+1;
}
\\最后得到的ans即为答案,如果ans=-1则无解
第二种:
分为两套。答案都处于\([l,r]\)中,循环以\(l=r\)结束,每次二分的中间值\(mid\)属于左右两段之一。
- 在单调递增序列\(a\)中查找第一个大于等于\(x\)的值的位置(\(x\)或\(x\)的后继)。
while(l<r){
int mid=l+r>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
//最终l即为答案
- 在单调递增序列\(a\)中查找最后一个小于等于\(x\)的值的位置(\(x\)或\(x\)的前驱)。
while(l<r){
int mid=l+r+1>>1;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
//最终l即为答案
总结一下两种形式:
-
缩小范围时,\(r=mid,l=mid+1\),取中间值时,\(mid=l+r\gg 1\)。
-
缩小范围时,\(l=mid,r=mid-1\),取中间值时,\(mid=l+r+1\gg 1\)。
接下来说明\(2\)中对\(mid\)的取法作区分是必须的。
假如\(2\)中同样采用\(mid=l+r\gg 1\),那么当\(r-l=1\)时,\(mid=l+r\gg 1=l\),此时如果\(l=mid\),区间未缩小;如果\(r=mid-1\),造成\(l>r\),循环不能以\(l=r\)结束。因此是必须的。
注意必须采用\(\gg\)而非\(/\),这是因为\(\gg\)向下取整,而\(/\)向零取整,当\([l,r]\)含负数时后者无法正常工作。
特别的作用:我们观察一下,\(mid=l+r\gg 1\)取不到\(r\),\(mid=l+r+1\gg 1\)取不到\(l\),可以借此处理无解的情况。将最初的区间向左/向右拓宽一位,包含一个越界的下标。如果最后的答案停留在这个越界的下标上,就说明无解。
终止时\(l=r\),若有解,此处便是答案。
实数域上二分
比整数域上的简单多了
首先要确定二分精度\(eps\),以\(l+eps<r\)为循环条件,每次依据\(mid\)处的判定选择\(l=mid\)或者\(r=mid\)分支之一即可。
一般来说,要保留\(k\)位小数时,设置\(eps=10^{-(k+2)}\)。
注意此时就只能使用\(/\)了。
while(l+eps<r){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
也可以用固定循环次数的二分,精度通常比设置\(eps\)高。
for(int i=0;i<100;++i){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
二分的重要思想
最优化问题也能抽象为函数。
要二分答案,函数就要有单调性,即在一边都不合法,一边都合法。
把求最优解的问题转化为判定是否能构造出解为\(mid\)的方案。
wqs二分
去看DP优化。
倍增
一般走路题就是倍增。比如说在一个序列 / 树 / 图上走路,然后沿途统计一些东西的。
遇见这种题时,如果没有别的想法,那只能是倍增一下了。但是首先应该能简单地做出第一次遇到某种事件的情况。
前缀和与差分
高维前缀和。二进制情况下或许也可以认为是 FWT or 卷积。

浙公网安备 33010602011771号