21天零基础入门ACM
21天零基础入门ACM之 第7天
二分
二分
二分是什么
二分应该是很常见的一种检索的方法了,二分的最常见场景就是在一个有序的序列里面找到你的目标数字。
通过多次判断中间的数字是否满足要求,而减少区间,时间复杂度是o(nlogn)。
二分的注意事项
- 二分运用的条件是有序,很多题目的有序是隐含的。
- 二分若出现多个符合目标条件的值,是取最左边的,还是最右边的。
- 注意死循环。
二分的模板
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
ans = mid;
r = mid-1;
}
else{
l=mid+1;
}
}
例题
例题1:
https://ac.nowcoder.com/acm/problem/23049
思路:
就是简单的通过二分找到答案。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int a[maxn];
int n,m;
bool check(int k)
{
if(k==0) return true;
ll ans=0;
for(int i=0;i<n;i++)
{
ans+=a[i]/k;
}
if(ans>=m) return true;
else return false;
}
int main()
{
while(cin>>n>>m)
{
int c=0;
for(int i=0;i<n;i++)
cin>>a[i];
int l=0,r=1e9+7,ans=0,mid;
while(l<r)
{
mid=(l+r)/2;
if(check(mid))
{
ans=max(ans,mid);
l=mid+1;
}
else r=mid;
}
cout<<ans<<endl;
}
return 0;
}
例题2
思路
这道题可能没这么简单的看出来是二分,题意要找到最小的人数,就是隐含的有序的条件。
代码
#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define sc(x) scanf("%d",&x)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; }
ll qpow(ll a, ll b, ll mod) { ll ans = 1; a%=mod; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; }
const int mod=1e9+7;
const int N=1e5+7;
int n,m;
int nums[N];
map<int,int>mp;
bool check(int mid){
int sum=0;
for(auto it:mp){
sum+=ceil(it.second*1.0/mid);
}
if(sum<=m) return true;
else return false;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>nums[i];
mp[nums[i]]++;
}
int ans=N,l=0,r=n;
while(l<=r){
int mid=(l+r)>>1;
if(mid==0) break;
if(check(mid)){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
if(ans!=N){
cout<<ans<<endl;
}
else cout<<"-1"<<endl;
}
例题3:
https://ac.nowcoder.com/acm/contest/8827/H
思路
两种操作:
- 更新某点的值
- 查询最小值为x的连续子序列有多少个。
更新不要说,查询的话就是找某个数左边和右边的连续的比他大的数有多少个,然后乘起来。
其实只需要线段树维护最小值,二分区间长度,查找第一个小于 给定值 的数的位置。
代码如下
#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define sc(x) scanf("%d",&x)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; }
ll qpow(ll a, ll b, ll mod) { ll ans = 1; a%=mod; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; }
const int mod=1e9+7;
const int N=1e5+7;
int n, m;
int nums[N], tree[N * 4];
void pushup(int k) { tree[k] = min(tree[k << 1], tree[k << 1 | 1]); }
void build(int k, int l, int r) {
if (l == r) {
tree[k] = nums[l];
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
pushup(k);
}
void updata(int pos, int val, int k, int l, int r) {
if (l == r) {
nums[pos] = val;
tree[k] = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)
updata(pos, val, k << 1, l, mid);
else
updata(pos, val, k << 1 | 1, mid + 1, r);
pushup(k);
}
int query(int k, int l, int r, int L, int R) {
if (l >= L && r <= R)
return tree[k];
int mid = (l + r) >> 1;
int ans = INT_MAX;
if (L <= mid)
ans = min(ans, query(k << 1, l, mid, L, R));
if (R > mid)
ans = min(ans, query(k << 1 | 1, mid + 1, r, L, R));
return ans;
}
int main() {
while (cin >> n >> m) {
for (int i = 1; i <= n; i++)
cin >> nums[i];
build(1, 1, n);
// for(int i=1;i<=50;i++) cout<<tree[i]<<" ";
while (m--) {
int op, x, y;
cin >> op;
if (op == 1) {
cin >> x >> y;
updata(x, y, 1, 1, n);
} else {
cin >> x;
int l, r, lans, rans;
l = 1, r = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (query(1, 1, n, mid, x) >= nums[x])
r = mid - 1;
else
l = mid + 1;
}
lans = l;
l = x, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (query(1, 1, n, x, mid) < nums[x])
r = mid - 1;
else
l = mid + 1;
}
rans = r;
cout << 1LL * (x - lans + 1) * (rans - x + 1) << endl;
}
}
}
}