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. ceph 16.2.15(Pacific)编译

    目录 获取ceph源码 编译 拉取submodule网络问题 安装依赖 do_cmake.sh 编译 vstart启动 问题 编译dashboard 安装nodejs 方法一 下载编译好的源码包 方法 ...

  2. Iceberg常用命令

    一.登录spark客户端 spark-sql --master yarn \ --deploy-mode client \ --queue default \ --name wang \ --driv ...

  3. 在IDEA如何使用JProfiler性能分析

    一.下载地址 https://www.ej-technologies.com/download/jprofiler/files 版本:11 激活码:L-J11-Everyone#speedzodiac ...

  4. [大模型/AI/GPT] Chatbox:大模型可视化终端应用

    序 概述:Chatbox AI Chatbox AI 是一款 AI 客户端应用和智能助手,支持众多先进的 AI 模型和 API,可在 Windows.MacOS.Android.iOS.Linux 和 ...

  5. java list集合去重的两种方式

  6. 当openlayers的地图所在的div的尺寸发生变化后,鼠标滚轮缩放地图发生偏移问题以及地图变形问题解决

    当地图的尺寸发生变化后,要调用map.updateSize()方法,强制使openlayers重新计算地图视口 updateSize() Force a recalculation of the ma ...

  7. C# 生成缩略图方法

    private static string CreateThumbnail(string filepath, int tWidth, int tHeight) { if (string.IsNullO ...

  8. 一些Qt样式设计的小积累

    QRadioButton 的设计 QRadioButton分有两个部分,由按钮和背景文字组成. QRadioButton::indicator { ...; // 设置你想要的属性 } QRadioB ...

  9. Alibaba Sentinel SSRF漏洞分析(CVE-2021-44139)

    Alibaba Sentinel SSRF漏洞分析(CVE-2021-44139) 一.Alibaba Sentienl 简介 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel ...

  10. nginx 配置 vue History模式

    解决 需要加一行 try_files $uri $uri/ /index.html;,其中 /index.html 是你自己的目录中的入口文件 server { listen [::]:80 defa ...