2025dsfz集训Day2:二分与三分
DAY2:二分与三分
\]
特别感谢 此次课的主讲 - 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\)
- 洛谷P2249 |【深基13.例1】查找
思路: 直接二分套模版
#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,×);
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\)
- AccodersP2046 |【一本通提高篇二分与三分】数列分段II
- 洛谷P1182 | 数列分段 Section II
思路: 其实和数列分段I
相比就多了个二分答案,判断过程都是一样的,需要吸氧(O2)
#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\)
- AccodersP1902 |【一本通基础分治】河中跳房子
思路: 二分房子之间的距离
#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\)
- AccodersP2043 |【一本通提高篇二分与三分】愤怒的牛
思路: 很经典的二分答案,与跳石头类似,二分每个牛棚之间的最短距离,枚举判断答案是否合法
#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\)
- AccodersP2044 |【一本通提高篇二分与三分】Best Cow Fences
- 洛谷P10450 | [USACO03MAR] Best Cow Fences G
思路: 依然是二分答案,但由于扯上了浮点数,所以还要维护精度,这样操作就麻烦了起来,二分出平均数,枚举,记录每个数与平均数的差值,求一个和,如果这个和 \(≥\) \(0\) ,那么这个答案就是合法的,只要这个答案对一个区间合法即可,因为题目中说的是是否存在,有一个合法,那就是存在,其他就是精度处理问题了
#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\)
洛谷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)是一种用于在单峰函数中寻找极值的优化算法。它通过将搜索区间分为三个部分并比较函数在两个划分点的取值,逐步缩小搜索范围来确定极值的位置。(求解带曲线的多次函数)
三分算法的基本思路如下:
初始化搜索区间,通常是整个定义域的区间。
将搜索区间分为三个部分,通常采用两个划分点将整个区间分成三等份。
计算两个划分点的函数取值。
比较两个划分点的函数取值,如果其中一个划分点的函数值更大(或更小),则极值在该划分点的同侧。
根据比较结果更新搜索区间,将较小(或较大)的划分点所在的区间作为新的搜索区间。
重复步骤3到步骤5,直到搜索区间足够小,或达到一定的迭代次数。
返回搜索区间的中点作为极值的近似位置。
三分算法的时间复杂度为 \(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:二分与三分的更多相关文章
- HZNU-ACM寒假集训Day2小结 二分答案
Day2 ---二分 这里直接给出模板 两种对应不同的情况 可以借助数轴理解 int bsearch_1(int l, int r) { while (l < r) { ; if (check( ...
- YBT 1.2 二分与三分
二分与三分: 二分(单调性),三分(单峰性),这章主要考数学,模拟,与读题!!! 链接: https://pan.baidu.com/s/1AJTl_0p5Lh4T1MmNwyJWzg 密码: j3j ...
- WC集训DAY2笔记 组合计数 part.1
目录 WC集训DAY2笔记 组合计数 part.1 基础知识 组合恒等式 错排数 卡特兰数 斯特林数 伯努利数 贝尔数 调和级数 后记 补完了几天前写的东西 WC集训DAY2笔记 组合计数 part. ...
- 2019暑期金华集训 Day2 线性代数
自闭集训 Day2 线性代数 高斯消元 做实数时,需要找绝对值最大的作为主元,以获取更高精度. 在欧几里得环(简单例子是模合数)意义下也是对的.比如模合数意义下可以使用辗转相除法消元. 欧几里得环:对 ...
- 暑假集训Day2 互不侵犯(状压dp)
这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...
- ACM : HDU 2899 Strange fuction 解题报告 -二分、三分
Strange fuction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- Chapter2 二分与三分
T1 给一个N个数的序列,分成M段,每段最大值最小 sol:二分最大值,贪心Check T2 平面上n个点,每个点每s会向周围扩散一个单位长度,两个点联通当且仅当扩散有交点,问什么时候这n个点联通 s ...
- codeforces#403—B题(二分,三分)
B. The Meeting Place Cannot Be Changed time limit per test 5 seconds memory limit per test 256 megab ...
- 2022寒假集训day2
day1:学习seach和回溯,初步了解. day2:深度优化搜索 T1 洛谷P157:https://www.luogu.com.cn/problem/P1157 题目描述 排列与组合是常用的数学方 ...
- 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 ...
随机推荐
- AI编程:如何编写提示词
这是小卷对AI编程工具学习的第2篇文章,今天讲讲如何编写AI编程的提示词,并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是:目标清晰明确,具有针对性,能引导模型理解问题 下面是 ...
- 独立开发经验谈:我是如何借助 Docker 环境变量让客户 1 分钟上线客服系统的
我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统.陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户,在这个过程中,我也积累了不少如何开发运营 ...
- Windows11本地部署DeepSeek加速
技术背景 在上一篇文章中我们介绍了在Ubuntu Linux操作系统上部署了一个DeepSeek-R1:14B,再通过其他电脑远程调用模型进行生成的方法.这里我们介绍一下Windows11安装Olla ...
- Q:linux上某个磁盘挂载不上
想把新创建的 /dev/datavg/data_lv 挂载到 /mysql 目录上 mkfs -t xfs -f /dev/datavg/data_lv mount /dev/datavg/data_ ...
- Johnson 全源负权最短路径算法详解
Floyd-Warshall算法可以求解出图内任意两点的最短路径,适用于稠密图,但时间复杂度为 \(O(n³)\):Dijkstra算法求解单源最短路径的时间复杂度为 \(O(m + n log n) ...
- [BZOJ4833] 最小公倍佩尔数 题解
在这篇题解中,我会将各个部分的证明分成不同的推导过程,以达到逐一击破的效果. 引理 1:\(f(n)=2f(n-1)+f(n-2)\) 我的证明挺繁琐的,过程如下: \[(1+\sqrt 2)^{n- ...
- Processing多窗口程序范例(三)
再来一例~另一种叠加方式呈现...看图: 程序 主程序: package syf.demo.multiwindow3; import processing.core.PApplet; import p ...
- new vue 实例发生了什么呢?
前言 最近全面栽进vue源码解析中,将出一系列的学习笔记 以及个人的一些总结 第一步准备工作 到GitHub上下载vue的源码(巧妇难为无米之炊) 用自己喜欢的编辑器打开源码 vue主要源码資源在sr ...
- 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 ...
- NumPy学习8
今天学习了NumPy统计函数 16,NumPy统计函数 numpy_test8.py : import numpy as np ''' 16,NumPy统计函数 NumPy 提供了许多统计功能的函数, ...