2021/7/14 二分专题

1、分治+二分
A - 4 Values whose Sum is 0
https://vjudge.net/contest/446761#problem

#include <iostream>
#include <algorithm>
#include <stdio.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 4e3+10;
int a[maxn],b[maxn],c[maxn],d[maxn];
ll sumcd[maxn*maxn];
ll sumab=0;
int main(){
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for(int i=1; i<=n; i++){
		cin >> a[i] >> b[i] >> c[i] >> d[i]; //输入的技巧,可以不用创二维数组
	}
	int k=0;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			sumcd[++k]=c[i]+d[j];//分治,先求其中两个的和的组合数组
		}
	}
	ll sum=0;
	sort(sumcd+1,sumcd+1+k);//用二分,先排序
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			sumab=a[i]+b[j];
			sum+=upper_bound(sumcd+1,sumcd+1+k,-sumab)-lower_bound(sumcd+1,sumcd+1+k,-sumab);
		}
	}
	cout << sum;
	return 0;
}

知识点:
lower_bound:
若找到,返回第一个大于等于查找值的下标,一般就是下界
若找不到,返回以数组长度为下标的下标值。
upper_bound:
若找到,返回第一个大于查找值的下标,一般是上界
若找不到,返回以数组长度为下标的下标值。

一般上界减下界就是要查找值的个数

同类题:
C - Can you find it?
https://vjudge.net/contest/446761#problem/C

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 510;
int l, n, m, k, h=0;
int a[maxn], b[maxn], c[maxn], d[maxn*maxn], e[maxn];
int main(){
	while(scanf("%d%d%d",&l,&n,&m)!=EOF){
		for(int i=0; i<l; i++){
			scanf("%d",&a[i]);
		}
		for(int i=0; i<n; i++){
			scanf("%d",&b[i]);
		}
		for(int i=0; i<m; i++){
			scanf("%d",&c[i]);
		}
		int g=0;
		for(int i=0; i<l; i++){
			for(int j=0; j<n; j++){
				d[g++]=a[i]+b[j];
			}
		}
		sort(d,d+g);
		printf("Case %d:\n",++h);
		scanf("%d",&k);
		while(k--){
			int x;
			scanf("%d",&x);
			int flag=1;
			for(int j=0; j<m; j++){
				if(c[j]<0){
					if(binary_search(d,d+g,x)){
						flag=0;
						break;
					}	
				}
				else{
					if(binary_search(d,d+g,x-c[j])){
						flag=0;
						break;
					}	
				}
			}
			if(flag) puts("NO");
			else puts("YES");
		}
	}
	return 0;
}

知识点:
binary_search:
若找到,返回true,否则返回false。

2、精度二分和技巧
B - Pie
https://vjudge.net/contest/446761#problem/B

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAX 0x3f3f3f3f
using namespace std;
const int maxn = 1e4+10;
const double pi=acos(-1.0);
typedef long long ll;
double a[maxn];
int n, f, t;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&f);
		f++;//要++,因为还有自己
		memset(a,0,sizeof(a));
		double sum=0;
		double max=0;
		for(int i=1; i<=n; i++){
			scanf("%lf",&a[i]);
			a[i]=a[i]*a[i]*pi;
		}
		sort(a+1,a+1+n);
		double low=0, high=a[n];
		double mid;
		while(high-low>0.00001){//在一定精度内要继续二分
			mid=(high+low)/2;
			int sumx=0;
			int flag=1;
			for(int i=1; i<=n; i++){
				sumx+=int(a[i]/mid);//注意,这里直接看能分几个
			}
			if(sumx<f) flag=0;//分的个数是否大于人
			if(flag) low=mid;//大于人直接以他为下界,继续二分
			else high=mid;//否则为上界,继续二分
		}
		printf("%.4f\n",mid);	
	}
	return  0;
}

//精度二分,low和high一般都直接等于mid,和下标二分不同,该题注意要用scanf和printf,否则会超时

3、打表二分
D - A Cubic number and A Cubic Number
https://vjudge.net/contest/446761#problem/D

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
ll t, p;
ll a[maxn];
int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld",&p);
		ll k=0;
		for(ll i=1; i<=1000000; i++){//要用立方差公式解方程
			a[k++]=i*i+i*(i+1)+(i+1)*(i+1);
		}
		if(binary_search(a,a+k,p)) puts("YES");
		else puts("NO");
	}
	return 0;
}

E - Funky Numbers
https://vjudge.net/contest/446761#problem/E

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5;
int a[maxn];
int main(){
	int n;
	cin >> n;
	int k=0;
	for(int i=1; (i*(i+1))/2<=1000000000; i++){//范围通过数学方法求出
		a[k++]=(i*(i+1))/2;
	}
	int flag=1;
	for(int i=0; i<k; i++){
		if(binary_search(a,a+k,n-a[i])){
			puts("YES");
			flag=0;
			break;
		}
	}
	if(flag) puts("NO");
	return 0;
}

可以看到,以上两题都是把要的结果直接存进数组中排序,然后再二分寻找答案。

4、普通二分找答案
F - Burning Midnight Oil
https://vjudge.net/contest/446761#problem/F

#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n, k;
int check(int v){
	int ans=v;
	while(v/k){
		v/=k;
		ans+=v;	
	}
	return ans;
}
int main(){
	cin >> n >> k;
	int l=1, r=n;
	while(r>l){//注意一定是r>l,不能是大于等于,会产生死循环
		int mid = (l+r)/2;
		if(check(mid)>=n) r=mid;//用r记录答案
		else l=mid+1;
	}
	cout << r;
	return 0;
}

5、二分法解方程
G - Can you solve this equation? 
https://vjudge.net/contest/446761#problem/G
核心思想:先判断是否单调,然后才能用二分法解方程,同时排除边界的情况
```cpp
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
double y;
double solve(double mid){
	return 8*pow(mid,4)+7*pow(mid,3)+2*pow(mid,2)+3*mid+6;
}
int main(){
	int t;
	cin >> t;
	while(t--){
		scanf("%lf",&y);
		double l=0, r=100;
		double mid;
		double k= solve(100);
		if(y<6 || y>k){ //先一步判断
			puts("No solution!");
			continue;
		}
		while(fabs(solve(mid)-y)>1e-4){//在一定精度里即可得到解
			mid=(l+r)/2;
			if(y<solve(mid)) r=mid;
			if(y>solve(mid)) l=mid;
		}
		printf("%.4f\n",mid);
	}
	return 0;
}

6、二分法解体积问题
核心思想:一般通过高进行二分,浮点二分
H - Cup
https://vjudge.net/contest/446761#problem/H

#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
const double pi=acos(-1.0);
int t;
double rb, rt, h, v;
double solve(double rb, double rx, double mid){
	return pi*mid*(rb*rx+rb*rb+rx*rx)/3;
}
int main(){
	cin >> t;
	while(t--){
		cin >> rb >> rt >> h >> v;//注意是杯子,上口大下口小
		double l=0, r=h;
		double rx=0;
		double mid=0;
		while(r-l>1e-8){
			mid=(l+r)/2;
			rx=(rt-rb)*(mid/h)+rb;
			if(solve(rb,rx,mid)>v) r=mid;
			else if(solve(rb,rx,mid)< v) l=mid;
			else break; //最好这样,优化了一点
		}
		printf("%.6f\n",mid);
		//这里最好输出mid,但因为精度太高了,所以输出l和r中任何一个都可以,对取值不会影响
	}
	return 0;
}

7、二分法与导数问题
I - Strange fuction
https://vjudge.net/contest/446761#problem/I

核心思想:该题先求导,可以看出x部分单调递增,故找到导函数零点,该点就是最小值点。

#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
int t;
double y;
double solve(double x){
	return 42*pow(x,6)+48*pow(x,5)+21*pow(x,2)+10*x;
}
double real(double x){
	return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-x*y;
}
int main(){
	cin >> t;
	while(t--){
		cin >> y;
		double l=0, r=100;
		double mid;
		while(r-l>1e-6){
			mid=(l+r)/2;
			if(solve(mid)>y) r=mid;
			else if(solve(mid)<y) l=mid;
			else break;
		}
		printf("%.4f\n",real(mid)); //注意是求值,不要用求导的式子来算
	}
	return 0;
}

8、二分+前缀和
K - Hard Process
https://vjudge.net/contest/446761#problem/K

9、炮台
J - Shell Pyramid
https://vjudge.net/contest/446761#problem/J

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
int i,t;
ll a[maxn];
ll sum[maxn];
int main()
{
    ios::sync_with_stdio(false);//用于关闭cin和cout的同步,使得cin和cout效率与scanf、prinft差不多,这个题里这行没啥用,写习惯了
    a[1]=1;
    for(i=2;i<maxn;i++)
    {
        a[i]=a[i-1]+i;
    }
    sum[1]=1;
    for(i=2;i<maxn;i++)
    {
        sum[i]=sum[i-1]+a[i];
    }
    cin>>t;
    while(t--)
    {
          ll number;
          cin>>number;
          ll ans1 = lower_bound(sum,sum+maxn,number)-sum;
          number = number - sum[ans1-1];
          ll ans2 = lower_bound(a,a+maxn,number)-a;
          ll ans3 = number - a[ans2-1];
          cout<<ans1<<" "<<ans2<<" "<<ans3<<endl;
    }
    return 0;
}

二分思想:
浮点二分
while(r-l>0.000001){
l=mid;
r=mid;…
}
正常数组二分
https://www.acwing.com/blog/content/31/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值