Loading

【一本通基础+提高】二分、三分与分治做题记录

【一本通基础+提高】二分、三分与分治做题记录

【一本通基础】分治算法做题记录

题目编号 标题 估分 正确 提交
1885 Problem  A 【一本通基础分治】循环比赛日程表 --- 115 522
1886 Problem  B 【一本通基础分治】 取余运算 --- 225 839
1887 Problem  C 【一本通基础分治】黑白棋子的移动 --- 164 358
1888 Problem  D 【一本通基础分治】光荣的梦想 --- 123 563
1889 Problem  E 【一本通基础分治】2011 --- 106 368
1890 Problem  F 【一本通基础分治】输出前k大的数 --- 174 627
1891 Problem  G 【一本通基础分治】区间合并 --- 128 333
1892 Problem  H 【一本通基础分治】求排列的逆序数 --- 142 199
1893 Problem  I 【一本通基础分治】查找最接近的元素 --- 89 448
1894 Problem  J 【一本通基础分治】NOIP2007 统计数字 --- 104 215
1896 Problem  K 【一本通基础分治】二分法求函数的零点 --- 217 384
Y 1897 Problem  L 【一本通基础分治】网线主管 --- 189 635
Y 1898 Problem  M 【一本通基础分治】月度开销 --- 184 466
Y 1899 Problem  N 【一本通基础分治】和为给定数 --- 112 388
Y 1900 Problem  O 【一本通基础分治】不重复地输出数 --- 89 250
1901 Problem  P 【一本通基础分治】膨胀的木棍 --- 44 143
Y 1902 Problem  Q 【一本通基础分治】河中跳房子 --- 144 424

Problem A

Problem B

Problem C

Problem D

Problem E

Problem F

Problem G

Problem H

Problem I

Problem J

Problem K

Problem L

  • 实数二分。注意 \(eps\) 的值(也就是误差)。二分分段的网线的长度。千万要注意是实数二分!要用 \(double\) !!
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,k;
double a[101010];
double l = 0,r,eps = 1e-5;
bool check(double mid){
	int ans = 0;
	for(int i = 1;i <= n;i++){
		ans+=(a[i]/mid);
	}
	return ans>= k;
}
int main(){
	cin>>n>>k;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
		r += a[i];
	}
	while(r-l > eps){
		double mid = (l+r)/2.0;
		if(check(mid)){
			l = mid;
		}else r = mid;
	}
    if(r < 0.01) r = 0.00;
	cout<<fixed<<setprecision(2)<<floor(r*100.0)/100.00;
}

Problem M

  • 二分答案。二分最大值,如果可以满足就将该值增大,否则就将该值减小。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[1010100];
int n,l = INT_MAX,r,m;
bool check(int mid){
	int now = 0;
	int ans = 1;
	for(int i = 1;i <= n;i++){
		if(a[i] > mid) return false;
		if(a[i]+now > mid){
			now = a[i];
			ans++;
		}else now += a[i];
	}
	return ans <= m;
}
int main(){
	cin>>n>>m;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
		r += a[i];
		l = min(l,a[i]);
	}
	while(l <= r){
		int mid = l+r>>1;
		if(check(mid)){
			r = mid-1;
		}else{
			l = mid+1;
		}
	}
	cout<<l<<endl;
}

Problem N

  • 不是二分。双指针例题。这里蒟蒻使用 \(map\) 解决。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
map <int,int> mp;
int n;
int a[101010];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin>>a[i];
		mp[a[i]]++;
		
	}
	int k;
	cin>>k;
	bool f = false;
	int x = 0,y = 0,z = INT_MAX;
	for(int i = 1;i <= n;i++){
		if(mp[k-a[i]] > 0) {
			f = 1;
			if(z > min(a[i],k-a[i])){
				z = min(a[i],k-a[i]);
				y = max(a[i],k-a[i]);
				x = min(a[i],k-a[i]);
			}
		}
	}
	if(f == 0 or (x == 1 and y == 1)) puts("No");
	else{
		cout<<x<<" "<<y;
	}
}

Problem O

  • 根本就不是分治。先把整个数列排序(\(sort\)),然后再去重(\(unique\))。就得到答案了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;

int a[1010101];
int n;
int main(){
	cin>>n;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
	}
	sort(a+1,a+1+n);
	n = unique(a+1,a+1+n)-a-1;
	for(int i = 1;i <= n;i++){
		cout<<a[i]<<" ";
	}
}

Problem P

Problem Q

  • 二分答案:直接二分最长跳跃距离,如果能够满足条件,就增加跳跃距离,否则就减小跳跃距离。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int L,n,m;
int a[2101010];
int ans;
inline bool check(int mid){
	int times = 0,ip = 0;
	for(int i = 1;i <= n+1;i++){
		if(a[i]-a[ip] < mid){
			times++;
			if(times > m) return false;
		}else ip = i;
	}
	return times <= m;
}
int main(){
	cin>>L>>n>>m;	
	a[n+1] = L;
	int l = 1,r = L;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
		l = min(l,a[i]-a[i-1]);
	}
	l = min(l,L-a[n]);
	while(l <= r){
		int mid = l+r>>1;
		if(check(mid)){
			l = mid+1;
		}else r = mid-1;
	}
	cout<<l-1<<endl;
	return 0;
}

【提高篇】二分与三分做题记录

题目编号 标题 估分 正确 提交
Y 2043 Problem  A 【一本通提高篇二分与三分】 愤怒的牛 --- 194 393
Y 2044 Problem  B 【一本通提高篇二分与三分】 Best Cow Fences --- 139 426
Y 2045 Problem  C 【一本通提高篇二分与三分】 曲线 --- 131 300
Y 2046 Problem  D 【一本通提高篇二分与三分】 数列分段II --- 125 298
Y 2047 Problem  E 【一本通提高篇二分与三分】 扩散 --- 103 241
Y 2048 Problem  F 【一本通提高篇二分与三分】 灯泡 --- 120 151
2049 Problem  G 【一本通提高篇二分与三分】传送带 --- 109 236

Problem A

  • 二分答案。二分牛和牛之间的距离。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[2101010];
int ans;
inline bool check(int mid){
	int dis = a[1],mm = 1;
	for(int i = 2;i <= n;i++){
		if(a[i]-dis >= mid){
			mm++;
			dis = a[i];
		}
	}
	return mm >= m;
}
int main(){
	cin>>n>>m;
	int l = 1,r = -99;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
	}
	sort(a+1,a+1+n);
	for(int i = 1;i <= n;i++){
		l = min(l,a[i]-a[i-1]);
	}
	r = a[n]-a[1];
	while(l <= r){
		int mid = l+r>>1;
		if(check(mid)){
			l = mid+1;
		}else r = mid-1;
	}
	cout<<l-1<<endl;
	return 0;
}

Problem B

  • 实数二分。其实双指针也能做。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[101010],n,L;
double b[101010];
int main(){
	cin>>n;
	cin>>L;
	int maxx = 0;
	for(int i = 1;i <= n;i++) cin>>a[i],maxx = max(maxx,a[i]);
	double l = 0,r = maxx*1.0,mid;
	while(r-l > 1e-5){
		mid = (l+r)/2;
		for(int i = 1;i <= n;i++) 
		b[i] = (b[i-1]+a[i]*1.0-mid)*1.0;
		double ans = 0;
		bool fl = 0;
		for(int i = L;i <= n;i++){
			ans = min(ans,b[i-L]);
			if(b[i]-ans >= 0.0){
				fl = 1;
				break;
			}
		}
		if(fl == 1) l = mid;
		else r = mid;
	}
	cout<<(int)(r*1000)<<endl;
}

Problem C

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n;
double a[101010],b[101010],c[101010];
double f(double x){
	double mx = -1e9;
	for(int i = 1;i <= n;i++) mx = max(mx,a[i]*x*x+b[i]*x+c[i]);
	return mx;
}
int main(){
	int T;
	cin>>T;
	while(T--){
		scanf("%d",&n);
		for(int i = 1;i <= n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
		double l = 0,r = 1000;
		while(r-l>1e-11){
			double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
			if(f(m1) <= f(m2)) r = m2;
			else l = m1;
		}
		printf("%.4lf\n",f(r));
	}
}

Problem D

  • 二分答案。二分分段的最大值。
点击查看代码
#include <bits/stdc++.h>
	using namespace std;
	
	int n,l = INT_MIN,r,m;
	int a[1010001]; 
	
	inline bool check(int mid){
		int tot = 0;
		int times = 1;
		for(int i = 1;i <= n;i++){
			if(tot+a[i] <= mid) tot += a[i];
			else tot = a[i],times++;
		}
		return times <= 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[i];
		}
		while(l <= r){
			int mid = l+r>>1;
			if(check(mid)) r = mid-1;
			else l = mid+1;
		}
		cout<<l<<endl;
		return 0;
	}

Problem E

  • 并查集+二分
点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct node{
    int x,y;
}a[1010101];
int f[1010101][2];
int n;

int find(int x){
    if(f[x][0] != x) return f[x][0] = find(f[x][0]);
    return x;
}

void merge(int x,int y){
    f[find(x)][0] = f[find(y)][0];
    f[find(y)][1] += f[find(x)][1];
}

bool check(int x){
    for(int i = 1;i <= n;i++){
        for(int j = i+1;j <= n;j++){
            if(abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y) <= x*2 and find(i) != find(j)) merge(i,j);
        }
    }
    int k = find(1);
    for(int i = 2;i <= n;i++){
        if(find(i) != k) return false;
    }
    return true;
}

int main(){
    cin>>n;
    for(int i = 1;i <= n;i++){
        cin>>a[i].x>>a[i].y;
    }
    int l = 0,r = 1e9;
    while(l <= r){
        for(int i = 1;i <= n;i++) f[i][0] = i,f[i][1] = 1;
        int mid = l+r>>1;
        if(check(mid)) r = mid-1;
        else l = mid+1;
    }
    cout<<l<<endl;
}

Problem F

  • 暂无。太难了!
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int t;
double hb,h,d;
double f(double x){
	double l = hb-(d*(hb-h)/x);
	return d-x+l;
}
int main(){
	cin>>t;
	while(t--){
		cin>>hb>>h>>d;
		double l = d-(h*d/hb),r = d;
		while(r-l >= 1e-5){
			double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
			if(f(m1) < f(m2)) l = m1;
			else r = m2;
		}
		printf("%.3lf\n",f(r));
	}	
}

Problem G

  • 三分套三分。分别在 \(AB\)\(CD\) 上找两个满足条件的点。
#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
int P,Q,R;
struct node{double x,y;}A,B,C,D,a,b;
double dis(node a,node b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double cal(double p1,double p2) {
    a.x=A.x+(B.x-A.x)*p1;
    a.y=A.y+(B.y-A.y)*p1;
    b.x=C.x+(D.x-C.x)*p2;
    b.y=C.y+(D.y-C.y)*p2;
    return dis(A,a)/P+dis(a,b)/R+dis(b,D)/Q;
}

double get(double x) {
    double l=0,r=1;
    while(r-l>eps) {
        double M1=l+(r-l)/3.0,M2=r-(r-l)/3.0;
        if(cal(x,M1)>cal(x,M2)) l=M1; else r=M2;
    }
    return cal(x,l);
}

int main() {
    cin>>A.x>>A.y>>B.x>>B.y;
    cin>>C.x>>C.y>>D.x>>D.y;
    cin>>P>>Q>>R;
    double l=0,r=1;
    while(r-l>eps) {
        double M1=l+(r-l)/3.0,M2=r-(r-l)/3.0;
        if(get(M1)>get(M2)) l=M1; else r=M2;
    }
    printf("%.2lf",get(l));
}
//三分(a点)套三分(b点)
posted @ 2025-03-22 19:28  FrankWkd  阅读(27)  评论(0)    收藏  举报