【JLOI】01
新年首讲,不是吗?
二分答案!
CCF NOIP 普及组基础知识
接下来我们将用2节课时间来讲一下二分以及二分答案
【引入例题】
给定一个长度为n的序列,保证是从小到大排的。有m次询问,每次询问数组中x第一次出现的位置。如果没有出现x就输出-1
n、m<=100000
【思路】
普通做法
无脑n×m模拟,时间复杂度O(nm),0分
高级做法
二分答案
首先二分是有局限性的:当且仅当数组有序时才能二分答案
观察到题目中数组从大到小排
可以二分答案
来一个可爱的小图:
有两个指针,名字叫L和R,而它们则代表了答案所在的区间
十分容易理解?对吗?
接着,它们生了个小宝宝叫MID
而MID等于(L+R)/2 父母各取一半
现在我们判断一下MID所在元素是否小于等于x!
如果不合法:对不起,区间缩小,变成左区间!
否则:OK,再接再厉,变成右区间!
代码片:
int l=1,r=n,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(a[mid]<=x) ans=mid,l=mid+1;
else r=mid-1;
}
我们分析一下时间复杂度
n个数(n)
m次询问+二分查找(mlogn)
总时间复杂度:(n+mlogn)
如果m>n怎么办?
若数据规模为:m<=10000000,n<=100000,还行吗?
明显不行
怎么办?
预处理。
离散化每个数字,例如
原数组:1,1,2,3,5,8
离散化的值:1,1,2,3,4,5
没错,离散化的值就是这个序列去重后这个数字的排名
然后对于-1,我们可以用哈希的方式判断,也可以用map来判重,这些都不是重点!
入门结束!
【例题1】数列分段Section II
十分明显:我们可以二分他的答案,然后对MID判断,判断方式可以用贪心
贪心方法:超了就自建一组,没超就归进最后一组
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[100001];
int l=1,r=0,ans=0;
bool check(int mid)
{
int sum=1,k=0;
for(int i=1;i<=n;i++)
{
k+=a[i];
if(k>mid)
{
sum++;
k=a[i];
}
}
return sum>m;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),l=max(l,a[i]),r+=a[</