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/