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. GIS数据合集:作物、植被数据下载平台整理

      本文对目前主要的作物类型与产量.植被物候与指数数据产品的获取网站加以整理与介绍. 目录 4 植被农业数据 4.1 作物产量数据 4.1.1 SPAM 4.1.2 Aerial Intelligen ...

  2. Nodify学习 二:添加节点

    Nodify学习 一:介绍与使用 - 可乐_加冰 - 博客园 (cnblogs.com) Nodify学习 二:添加节点 - 可乐_加冰 - 博客园 (cnblogs.com) 添加节点(nodes) ...

  3. .NET周刊【1月第2期 2025-01-12】

    国内文章 [.NET] API网关选择:YARP还是Ocelot? https://www.cnblogs.com/madtom/p/18655530 本文详细比较了YARP和Ocelot两种API网 ...

  4. DeepSeek-R1 技术全景解析:从原理到实践的“炼金术配方” ——附多阶段训练流程图与核心误区澄清

    字数:约3200字|预计阅读时间:8分钟 (调试着R1的API接口,看着控制台瀑布般流淌的思维链日志)此刻我仿佛看到AlphaGo的棋谱在代码世界重生--这是属于推理模型的AlphaZero时刻. D ...

  5. Windows的MySQL数据库升级(解压包方式)

    1.背景描述 原来的 MySQL 在安装时,是最新的稳定版本 5.7.33 . 经过一段时间后,在原来的 MySQL 版本中,发现存在漏洞. 因为 MySQL 的官方补丁,需要 Oracle 的 si ...

  6. 分合之道:最小生成树的 Kruskal 与 Prim 算法

    最小生成树问题 想象你是一位城市规划师,面前摊开一张地图,标记着散落的村庄.你的任务是用最经济的成本,在村庄间铺设道路,让所有村庄互通.这个问题看似简单,却隐藏着一个经典的数学命题:如何在一张&quo ...

  7. Deepseek学习随笔(8)--- 清华大学发布Deepseek从入门到精通(附网盘链接)

    文档简介:AI时代的新工具手册 清华大学新闻与传播学院联合元宇宙文化实验室发布的<DeepSeek从入门到精通>,是一份系统化.实战性极强的AI工具学习指南.文档聚焦国产开源通用人工智能工 ...

  8. 解决win11输入法自定义短语有多个当前日期只有最后一个生效

    由于win11自定义短语保存机制问题导致只有最后一个当前日期短语生效** 1.首先找到自定义短语存储文件的具体路径:%AppData%\Microsoft\InputMethod\Chs\ 拼音输入法 ...

  9. [tldr] fish shell添加环境变量到配置文件

    fish shell配置文件的编写格式和位置都和bash不同 文件位置 位于~/.config/fish/config.fish 设置PATH fish shell不会去读取~/.bashrc文件中的 ...

  10. python pandas csv 大文件 DataFrame转换为List

    python 将大文件读取为 DataFrame 时,直接对整个文件进行读取会比较耗时,甚至内存还会不足. https://pandas.pydata.org/pandas-docs/stable/u ...