【金华集训 && 笔记】Day 7——二分

二分


一、二分查找:要求在log的时间内在一串数中找出其中一个数

  • 二分答案必须保证单调性,所以先要将序列排一个序
  • 每次将区间二二分,将值与中间的数进行比较,根据比较情况分区间查找
  • 这样的话必定能舍弃一个区间
  • 所以总的时间为 O ( l o g n ) O(logn) O(logn)

例:在一串序列中找出 ≤ x \leq x x的最大的那个数

int l=1,r=n;
while (l+1<r){
    int mid=l+r>>1;
    if (a[mid] == x) return mid;
    if (a[mid] > x) r = mid;else l = mid;
}
return a[r] <=x ? r:l
一、present
  • 每次二分答案最大化序列的最小值
    • 对原有的序列维护差分
  • 从左往右枚举,如果当前数<mid,那么就将 [ i − i + w − 1 ] [i-i+w-1] [ii+w1]的数所有都加上(mid-a[1])+1
  • 这个东西可以用队列维护
  • 这样遍历一遍复杂度是 O ( n ) O(n) O(n)
  • 总的复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
  • 似乎大致懂了
二、packmen
  • 二分时间
  • 考虑贪心
  • 从左往右扫,分情况讨论:
  • 1、如果扫到的是食物,那么就扫它右边的人,看距离是否小于mid
  • 2、如果扫到的是人,那么就考虑他右边的食物以及他左边的食物
  • 设他到食物的距离为d
  • 如果3d<mid,那么就先左后右,否则先右后左(画图解放陈即可)
  • 扫完一个人清空他以及他的路径
三、tallshoes
  • 二分鞋子的高度
  • 对于鞋子的高度进行重新建图
  • 如果当前边的高度小于鞋子的高度,那么就把边的边权定为高度上调 ( 鞋 子 − 高 度 ) 2 (鞋子-高度)^2 ()2,否则为0
  • 跑单元最短路,看最小值是否满足金币数即可
四、金字塔
  • 二分答案中位数之后将问题转化为01序列问题
  • 然后好像是根据是否连续、相邻讨论
  • 最中间的两个相邻的数即为答案
  • 如果没有相邻的数那么就根据层数奇偶性判断
  • 没怎么听懂

二分+dp

一、 levko and array
  • 考虑二分答案,二分差值(答案)
  • 修改不超过K个,即不修改的数 ≥ n − k ≥ n-k nk
  • d p I dp_I dpI表示 a i a_i ai不修改的情况下i-1最多有多少个数不用修改
  • d p j + 1 − &gt; d p i ( m i d ∗ ( j − i ) ≤ ∣ a [ i ] − a [ j ] ∣ ) dp_{j+1}-&gt;dp_i(mid*(j-i)\leq |a[i]-a[j]|) dpj+1>dpi(mid(ji)a[i]a[j])
二分求最长上升子序列
  • f I f_I fI表示长度为i的上升子序列末尾元素最小是多少
  • 能够证明,f数组具有单调性
  • 设m是当前上升子序列的长度
  • a i   &gt; f m a_i\ &gt;f_m ai >fm,则令 f [ + + m ]   =   a [ i ] f[++m]\ =\ a[i] f[++m] = a[i]
  • 如果想让 a i a_i ai满足作用,只有满足 f j   &lt;   a i   &lt;   f j + 1 f_j\ &lt;\ a_i\ &lt;\ f_{j+1} fj < ai < fj+1
  • a I a_I aI之多只有一个
  • 便可以通过二分答案来查找
Code
f[0] = -inf;
m = 0;
for (int i=1;i<=n;i++){
    if (a[i]>f[m]) f[++m] = a[i];//能够直接更新 
    int res = 0 , l = 0,r = m;
    while (l+1<r){
	    int mid = l+r>>1;
	    if (f[mid] < a[i]) res = mid,l = mid;else r = mid;
	}//二分查找f_j<a[j]的 j,说明可以继续组合 
	if (f[l] < a[i]) res = l;else res = r;
	f[res+1] = min(f[res+1] , a[i]);//尽可能小 
}

线段树上二分

应用:求带修改全局第k大,区间第k大

  • 带修改全局第k大
    • 要求支持两种操作:单点修改与查询第k大元素
      Code
int ask(int p,int k){
	//线段树编号为p,查询第k大个数
    if (l == r) return tr[p].ans;
    int mid = l+r>>1;
    if (tr[p<<1].sz >= k)
      return ask(p<<1 , k);
    else return (p<<1|1,k-tr[p<<1].sz);//由于权值线段树权值单调递增,左边已经有了size个数,这是第k大就是右边找一个第k-size个数 
}

  • 区间k大
    • 要求支持一种操作:给出序列上的一个区间,求这个区间中权值第k大的数。
      • Code
int ask(int p,int x,int y,int k){
	//线段树编号为p,查询第k大个数
    if (l == r) return tr[p].ans;
    int mid = l+r>>1;
    int tmp = tr[x].sz - tr[y].sz;
    if (tmp >= k)
      return ask(p<<1 ,tr[x].l,tr[y].l, k);
    else return (p<<1|1,tr[x].r,tr[y].r,k-tmp);//由于权值线段树权值单调递增,左边已经有了size个数,这是第k大就是右边找一个第k-size个数 
}

整体二分

  • 巧妙的利用了离线的特点,把所有的修改、询问放在一起考虑
  • 在递归处理过程中本质是在做二分答案,批量的处理一串的操作和询问
  • 递归到底层是就返回值
一、待修改的区间第k大
  • 我屮艸芔茻 。。似乎没跟上
二、You are given a tree
  • 详情请看我昨天的笔记(虽然是假的)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值