DAY2:二分与三分

\[Designed\ By\ FrankWkd\ -\ Luogu@Lwj54joy,uid=845400
\]

特别感谢 此次课的主讲 - Kwling

二分概述

  • 二分法,在一个单调有序的集合或函数中查找一个解,每次分为左右两部分,判断解在那个部分并调整上下界,直到找到目标元素,每次二分都将舍弃一般的查找空间,因此效率很高。

二分常见模型

  • 二分答案

    最小值最大(或是最大值最小)问题,这类双最值问题常常选用二分法求解,也就是确定答案后,配合贪心,\(DP\) 等其他算法检验这个答案是否合理,将最优化问题转化为判定性问题。例如,将长度为 \(n\) 的序列 \(a_i\) 分为最多 \(m\) 个连续段,求所有分法中每段和的最大值的最小是多少?

  • 二分查找

    用具有单调性的布尔表达式求解分界点,比如在有序数列中求数字 \(x\) 的排名。

  • 代替三分(?存疑)

    有时,对于一些单峰函数,我们可以用二分导函数的方法求解函数的极值,这时通常将函数的定义域定义为整数域求解比较方便,此时 \(d_x\) 可以直接去整数 \(1\) 。

二分使用范围:

  • 必须具备单调性或者是二段性

参考leetcode暑假打卡活动2019——week1中

视频链接:传送门

写二分的过程:

  1、确定二分边界

  2、编写二分的代码框架

  3、设计一个check(性质)

  4、判断一下区间如何更新

  5、如果更新方式是 L = Mid , R = Mid - 1 ,那么在算mid时要加1

二分查找

  • 手写二分【模版】

    • 记得将无序数组变得有序!
    • 记得考虑无解的情况。
    • #include <bits/stdc++.h>
      using namespace std;
      int target,n;
      int a[1010001];
      int main(Designer = 洛谷@Lwj54joy,uid=845400){
      cin>>n>>target;
      for(int i = 1;i <= n;i++){
      cin>>a[i];
      }
      int l = 1,r = n;
      while(l <= r){
      int mid = l+r>>1;
      if(a[mid] < target) l = mid+1;
      else r = mid-1;
      }
      if(a[l] != target) cout<<-1<<endl;
      else cout<<l<<endl; }
  • 函数二分【模版】
    • 直接用lower_bound求左边界,超级短。
    • #include <bits/stdc++.h>
      using namespace std; int n,target;
      int a[1010001];
      int main(Designer = 洛谷@Lwj54joy,uid=845400){
      cin>>n>>target;
      for(int i = 1;i <= n;i++) cin>>a[i];
      if(binary_search(a+1,a+1+n,target) == true){
      int answer = lower_bound(a+1,a+1+n,target)-a;
      cout<<answer<<endl;
      } }

二分题目解法&思路

\(T1\)

#include <bits/stdc++.h>
using namespace std; int n;
int target,times;
int a[1010001];
int main(Designer = 洛谷@Lwj54joy,uid=845400){
scanf("%d%d",&n,&times);
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
}
while(times--){
scanf("%d",&target);
int l = 1,r = n;
while(l <= r){
int mid = l+r>>1;
if(a[mid] < target) l = mid+1;
else r = mid-1;
}
if(a[l] != target) printf("-1 ");
else printf("%d ",l);
} }

\(T2\)

#include <bits/stdc++.h>
using namespace std; int n,l = INT_MIN,r,m;
int a[1010001]; bool check(int mid){
int tot = 0;
int times = 1;
for(int i = 1;i <= n;i++){
if(tot+a[i] <= mid) tot += a[i];
else tot = a[i],times++;
}
return times <= m;
} int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n>>m;
for(int i = 1;i <= n;i++){
cin>>a[i];
l = max(l,a[i]);
r += a[i];
}
while(l <= r){
int mid = l+r>>1;
if(check(mid)) r = mid-1;
else l = mid+1;
}
cout<<l<<endl; }

\(T3\)

	#include <bits/stdc++.h>
using namespace std;
int L,n,m;
int a[2101010];
int ans;
inline bool check(int mid){
int times = 0,ip = 0;
for(int i = 1;i <= n+1;i++){
if(a[i]-a[ip] < mid){
times++;
if(times > m) return false;
}else ip = i;
}
return times <= m;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>L>>n>>m;
a[n+1] = L;
int l = 1,r = L;
for(int i = 1;i <= n;i++){
cin>>a[i];
l = min(l,a[i]-a[i-1]);
}
l = min(l,L-a[n]);
while(l <= r){
int mid = l+r>>1;
if(check(mid)){
l = mid+1;
}else r = mid-1;
}
cout<<l-1<<endl; }

\(T4\)

#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[2101010];
int ans;
inline bool check(int mid){
int dis = a[1],mm = 1;
for(int i = 2;i <= n;i++){
if(a[i]-dis >= mid){
mm++;
dis = a[i];
}
}
return mm >= m;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n>>m;
int l = 1,r = -99;
for(int i = 1;i <= n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
for(int i = 1;i <= n;i++){
l = min(l,a[i]-a[i-1]);
}
r = a[n]-a[1];
while(l <= r){
int mid = l+r>>1;
if(check(mid)){
l = mid+1;
}else r = mid-1;
}
cout<<l-1<<endl; }

\(T5\)

#include <bits/stdc++.h>
using namespace std;
int a[101010],n,L;
double b[101010];
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n;
cin>>L;
int maxx = 0;
for(int i = 1;i <= n;i++) cin>>a[i],maxx = max(maxx,a[i]);
double l = 0,r = maxx*1.0,mid;
while(r-l > 1e-5){
mid = (l+r)/2;
for(int i = 1;i <= n;i++)
b[i] = (b[i-1]+a[i]*1.0-mid)*1.0;
double ans = 0;
bool fl = 0;
for(int i = L;i <= n;i++){
ans = min(ans,b[i-L]);
if(b[i]-ans >= 0.0){
fl = 1;
break;
}
}
if(fl == 1) l = mid;
else r = mid;
}
cout<<(int)(r*1000)<<endl;
}

\(T6\)

  • AccodersP2047 |【一本通提高篇二分与三分】扩散

  • 洛谷P1661 | 扩散

    思路:

    一想到大数据的联通块,绝对是并查集。

    那么,题中联通的定义也就转化成了:这些点具有相同的公共祖先。即:

    \(F[find(i)] == k\)

    其中, \(k\) 为常量, \(i\) 为 \(1-n\) 中的任意值。

    那么,仔细想想就能想得到,直接二分扩散时间。因为每个点都在扩散,那么两点之间的曼哈顿距离必须小于等于扩散时间乘 \(2\) 才能保证两点在扩散中相遇。

    在 \(check\) 函数中,我们直接暴力枚举每个点,如果在 \(mid\) 时间及以前能够相遇,就在并查集中合并二者。最后,如果所有的点拥有相同的公共祖先,即 \(F[find(i)] == k\) ,就可以返回 \(true\) 了。

#include <bits/stdc++.h>
using namespace std;
struct node{
int x,y;
}a[1010101];
int f[1010101][2];
int n; int find(int x){
if(f[x][0] != x) return f[x][0] = find(f[x][0]);
return x;
} void merge(int x,int y){
f[find(x)][0] = f[find(y)][0];
f[find(y)][1] += f[find(x)][1];
} bool check(int x){
for(int i = 1;i <= n;i++){
for(int j = i+1;j <= n;j++){
if(abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y) <= x*2 and find(i) != find(j)) merge(i,j);
}
}
int k = find(1);
for(int i = 2;i <= n;i++){
if(find(i) != k) return false;
}
return true;
} int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>a[i].x>>a[i].y;
}
int l = 0,r = 1e9;
while(l <= r){
for(int i = 1;i <= n;i++) f[i][0] = i,f[i][1] = 1;
int mid = l+r>>1;
if(check(mid)) r = mid-1;
else l = mid+1;
}
cout<<l<<endl;
}

三分

  • 三分算法(Ternary Search Algorithm)是一种用于在单峰函数中寻找极值的优化算法。它通过将搜索区间分为三个部分并比较函数在两个划分点的取值,逐步缩小搜索范围来确定极值的位置。(求解带曲线的多次函数)

  • 三分算法的基本思路如下:

    1. 初始化搜索区间,通常是整个定义域的区间。

    2. 将搜索区间分为三个部分,通常采用两个划分点将整个区间分成三等份。

    3. 计算两个划分点的函数取值。

    4. 比较两个划分点的函数取值,如果其中一个划分点的函数值更大(或更小),则极值在该划分点的同侧。

    5. 根据比较结果更新搜索区间,将较小(或较大)的划分点所在的区间作为新的搜索区间。

    6. 重复步骤3到步骤5,直到搜索区间足够小,或达到一定的迭代次数。

    7. 返回搜索区间的中点作为极值的近似位置。

三分算法的时间复杂度为 \(O(logN)\) ,其中 \(N\) 为搜索区间的长度。它的优势在于可以在单峰函数中快速找到极值的位置,但不适用于非单调函数或多峰函数的寻优问题。

三分题目解法&思路

\(T1\)

  • 洛谷P1883 |【模板】三分 | 函数

    思路:

    观察题目图像得 \(F(x)\) 函数是一个下凸函数。即对于任意的 \(x1<x2<x3\) ,

    不存在 \(F(x1)<F(x2)>F(x3)\) ,根据这个定义,我们可以通过三分法求该函数的最值。

    随便画出二次函数的一段(二次函数是一个特殊的下凸函数),

    我们发现一个下凸函数其上四个点无非有以下几种情况:

    设下凸函数上四个点横坐标分别为 \(x1<x2<x3<x4\) ,

    并且我们已经确定该函数的最小值在 \([x1,x4]\) 之间

    若F(x1)<F(x2),则函数的最小值的x的取值范围一定在[x1,x2]之间,因为根据定义,

    若有 \(F(x1)<F(x2)\) ,必有 \(F(x1)<F(x2)<F(x3)<F(x4)\) ;

    若 \(F(x3)>F(x4)\) ,同理可得,函数最小值的取值范围一定是在 \([x3,x4]\) 之间。

    若 \(F(x1)>F(x2)\ \&\ F(x2)<F(x3)\) ,

    则函数的最小值的 \(x\) 的取值范围一定在 \([x1,x3]\) 之间,

    因为下凸函数有且仅有一个低谷,并且下凸函数的最小值在低谷中。

    若 \(F(x4)>F(x3)\ \&\ F(x3)<F(x2)\),

    同理,函数最小值的取值范围一定在 \([x2,x4]\) 之间。

    最后就只剩下一种情况了,此时函数最小值的取值范围一定在 \([x2,x3]\) 之间。

#include <bits/stdc++.h>
using namespace std;
int n;
double a[101010],b[101010],c[101010];
double f(double x){
double mx = -1e9;
for(int i = 1;i <= n;i++) mx = max(mx,a[i]*x*x+b[i]*x+c[i]);
return mx;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
double l = 0,r = 1000;
while(r-l>1e-11){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) <= f(m2)) r = m2;
else l = m1;
}
printf("%.4lf\n",f(r));
}
}

\(T2\)

  • AccodersP2045 |【一本通提高篇二分与三分】曲线

    思路: 由于函数 \(S\) 是开口向上的二次函数(当 \(a=0\) 时,是一次函数),由 \(S\) 的定义可知,\(S\) 或者是一个先单调减、后单调增的下凸函数,或者是一个单调函数,\(F(x)=max(S_x)\) 也满足单调性。选用三分法很容易求得某个区间内的最小值。
#include <bits/stdc++.h>
using namespace std;
int n;
double a[101010],b[101010],c[101010];
double f(double x){
double mx = -1e9;
for(int i = 1;i <= n;i++) mx = max(mx,a[i]*x*x+b[i]*x+c[i]);
return mx;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
double l = 0,r = 1000;
while(r-l>1e-11){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) <= f(m2)) r = m2;
else l = m1;
}
printf("%.4lf\n",f(r));
}
}

\(T3\)

  • AccodersP2048 |【一本通提高篇二分与三分】灯泡
  • 洛谷P5931 | [清华集训2015]灯泡

    思路:

    首先可以分两种情况:

    (1)有影子在墙上

    (2)没有影子在墙上

    没有影子在墙上的时候,通过计算可以得出当光线照在墙角的时候最大。

    设人到墙的距离为 \(x\)

    这个时候我们可以得到 \(x\) 的上界 \(h * D / H\) (相似)

    这个时候就可以合并到第一种情况。

    第一种情况可以推出影子长度 \(L = x + (D * h - x * H) / (D - x)\)

    不需要化简,只要在程序中可以算出就行了。

    有某个 \(x\) 是最大的。

    果然,有最值。
	#include <bits/stdc++.h>
using namespace std;
int t;
double hb,h,d;
double f(double x){
double l = hb-(d*(hb-h)/x);
return d-x+l;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>t;
while(t--){
cin>>hb>>h>>d;
double l = d-(h*d/hb),r = d;
while(r-l >= 1e-5){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) < f(m2)) l = m1;
else r = m2;
}
printf("%.3lf\n",f(r));
}
}

\(\huge Thanks.\)



感谢阅读,若有问题或错误,欢迎评论(或私信我)。

2025 Designed By @洛谷Lwj54joy,uid=845400

2025dsfz集训Day2:二分与三分的更多相关文章

  1. HZNU-ACM寒假集训Day2小结 二分答案

    Day2 ---二分 这里直接给出模板 两种对应不同的情况 可以借助数轴理解 int bsearch_1(int l, int r) { while (l < r) { ; if (check( ...

  2. YBT 1.2 二分与三分

    二分与三分: 二分(单调性),三分(单峰性),这章主要考数学,模拟,与读题!!! 链接: https://pan.baidu.com/s/1AJTl_0p5Lh4T1MmNwyJWzg 密码: j3j ...

  3. WC集训DAY2笔记 组合计数 part.1

    目录 WC集训DAY2笔记 组合计数 part.1 基础知识 组合恒等式 错排数 卡特兰数 斯特林数 伯努利数 贝尔数 调和级数 后记 补完了几天前写的东西 WC集训DAY2笔记 组合计数 part. ...

  4. 2019暑期金华集训 Day2 线性代数

    自闭集训 Day2 线性代数 高斯消元 做实数时,需要找绝对值最大的作为主元,以获取更高精度. 在欧几里得环(简单例子是模合数)意义下也是对的.比如模合数意义下可以使用辗转相除法消元. 欧几里得环:对 ...

  5. 暑假集训Day2 互不侵犯(状压dp)

    这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...

  6. ACM : HDU 2899 Strange fuction 解题报告 -二分、三分

    Strange fuction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  7. Chapter2 二分与三分

    T1 给一个N个数的序列,分成M段,每段最大值最小 sol:二分最大值,贪心Check T2 平面上n个点,每个点每s会向周围扩散一个单位长度,两个点联通当且仅当扩散有交点,问什么时候这n个点联通 s ...

  8. codeforces#403—B题(二分,三分)

    B. The Meeting Place Cannot Be Changed time limit per test 5 seconds memory limit per test 256 megab ...

  9. 2022寒假集训day2

    day1:学习seach和回溯,初步了解. day2:深度优化搜索 T1 洛谷P157:https://www.luogu.com.cn/problem/P1157 题目描述 排列与组合是常用的数学方 ...

  10. ACM:HDU 2199 Can you solve this equation? 解题报告 -二分、三分

    Can you solve this equation? Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Su ...

随机推荐

  1. AI编程:如何编写提示词

    这是小卷对AI编程工具学习的第2篇文章,今天讲讲如何编写AI编程的提示词,并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是:目标清晰明确,具有针对性,能引导模型理解问题 下面是 ...

  2. 独立开发经验谈:我是如何借助 Docker 环境变量让客户 1 分钟上线客服系统的

    我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统.陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户,在这个过程中,我也积累了不少如何开发运营 ...

  3. Windows11本地部署DeepSeek加速

    技术背景 在上一篇文章中我们介绍了在Ubuntu Linux操作系统上部署了一个DeepSeek-R1:14B,再通过其他电脑远程调用模型进行生成的方法.这里我们介绍一下Windows11安装Olla ...

  4. Q:linux上某个磁盘挂载不上

    想把新创建的 /dev/datavg/data_lv 挂载到 /mysql 目录上 mkfs -t xfs -f /dev/datavg/data_lv mount /dev/datavg/data_ ...

  5. Johnson 全源负权最短路径算法详解

    Floyd-Warshall算法可以求解出图内任意两点的最短路径,适用于稠密图,但时间复杂度为 \(O(n³)\):Dijkstra算法求解单源最短路径的时间复杂度为 \(O(m + n log n) ...

  6. [BZOJ4833] 最小公倍佩尔数 题解

    在这篇题解中,我会将各个部分的证明分成不同的推导过程,以达到逐一击破的效果. 引理 1:\(f(n)=2f(n-1)+f(n-2)\) 我的证明挺繁琐的,过程如下: \[(1+\sqrt 2)^{n- ...

  7. Processing多窗口程序范例(三)

    再来一例~另一种叠加方式呈现...看图: 程序 主程序: package syf.demo.multiwindow3; import processing.core.PApplet; import p ...

  8. new vue 实例发生了什么呢?

    前言 最近全面栽进vue源码解析中,将出一系列的学习笔记 以及个人的一些总结 第一步准备工作 到GitHub上下载vue的源码(巧妇难为无米之炊) 用自己喜欢的编辑器打开源码 vue主要源码資源在sr ...

  9. docker Get "https://registry-1.docker.io/v2/": x509: certificate is valid for

    前言 docker 在进行 build 时,报错:Get "https://registry-1.docker.io/v2/": x509: certificate is vali ...

  10. NumPy学习8

    今天学习了NumPy统计函数 16,NumPy统计函数 numpy_test8.py : import numpy as np ''' 16,NumPy统计函数 NumPy 提供了许多统计功能的函数, ...