基本技巧(长期更新)

贪心

贪心总结

分治

分治总结

二分

看到使最大值最小或者使最小值最大时都可以尝试上二分。

注意二分的问题要有单调性(在某一点之前都不满足,之后都满足)。

二分的\(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\)属于左右两段之一。

  1. 在单调递增序列\(a\)中查找第一个大于等于\(x\)的值的位置(\(x\)\(x\)的后继)。
while(l<r){
  int mid=l+r>>1;
  if(a[mid]>=x) r=mid;
  else l=mid+1;
}

//最终l即为答案
  1. 在单调递增序列\(a\)中查找最后一个小于等于\(x\)的值的位置(\(x\)\(x\)的前驱)。
while(l<r){
  int mid=l+r+1>>1;
  if(a[mid]<=x) l=mid;
  else r=mid-1;
}

//最终l即为答案

总结一下两种形式:

  1. 缩小范围时,\(r=mid,l=mid+1\),取中间值时,\(mid=l+r\gg 1\)

  2. 缩小范围时,\(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 卷积。

启发式搜索和迭代加深

DLX

posted @ 2024-07-26 08:23  RandomShuffle  阅读(37)  评论(0)    收藏  举报