8.17日二分测试总结

8.17日二分测试总结

比赛传送门

分数情况

A.砍树 B.买木头 C.数列分段2 D.吃冰棍 E.跳石头 F.奶牛晒衣服
100 80 100 \(_{没做:(}\) 10 0

总体分数

微信图片 20240818105911

\(_{很惨}\)

T1. P1873 [COCI 2011/2012 #5] EKO / 砍树

题目传送门

问题分析

运用二分答案与 check 函数

check 函数用来检测答案是否合法

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 7;
int n, m;
int a[maxn];
bool check(int x)
{
	int ans = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (a[i] > x)
		{
			ans += (a[i] - x);
		}
	}
	return ans >= m;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int maxx = 0;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
		maxx = max(maxx, a[i]);
	}
	int l = 1, r = maxx + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	cout << l << "\n";
	return 0;
}

T2. B3880 [信息与未来 2015] 买木头

题目传送门

\(Wrong\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn], b[maxn];
int n, m;
bool check(int x)
{
	int sum = 0;
	for (int i = 1; i <= n; ++i)
	{
		sum += a[i] / x * b[i];
		if (sum >= m)
		{
			return 1;
		} 
	}
	return 0;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> a[1] >> b[1];
	int maxx = INT_MIN;
	for (int i = 2; i <= m; ++i)
	{
		a[i] = ((a[i - 1] * 37011 + 10193) % 10000) + 1;
		b[i] = ((b[i - 1] * 73011 + 24793) % 100) + 1;
		maxx = max(a[i], maxx);
	}
	int l = 0, r = maxx + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	cout << l << "\n";
	return 0;
}
/*
10 10000 8 20
*/

问题分析

通过题目给的两个公式:

  • \(l_i=((l_{i-1}\times37011+10193) \bmod 10000)+1\)
  • \(s_i=((s_{i-1}\times73011+24793) \bmod 100)+1\)

和给出的初始值求出后面的每个数的大小

通过二分答案与 check 函数查找合法的答案

错误分析

  • 细节决定成败

题目中说:

一行四个整数 \(n,m,l_1,s_1\)。其中 \(l_1\) 是第一个供货商木头的长度,\(s_1\) 是第一个供货商木头的数量。

而我的输入为什么是 \(m\) !!! \(_{气死我了}\)

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn], b[maxn];
int n, m;
bool check(int x)
{
	int sum = 0;
	for (int i = 1; i <= n; ++i)
	{
		sum += a[i] / x * b[i];
		if (sum >= m)
		{
			return 1;
		} 
	}
	return 0;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> a[1] >> b[1];
	int maxx = INT_MIN;
	for (int i = 2; i <= n; ++i)
	{
		a[i] = ((a[i - 1] * 37011 + 10193) % 10000) + 1;
		b[i] = ((b[i - 1] * 73011 + 24793) % 100) + 1;
		maxx = max(a[i], maxx);
	}
	int l = 0, r = maxx + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	cout << l << "\n";
	return 0;
}
/*
10 10000 8 20
*/

T3. P1182 数列分段 Section II

题目传送门

前置题目 P1181 数列分段 Section I

\(AC\) \(Code:\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn];
int n, m;

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int sum = 0, ans = 1;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
		if (sum + a[i] <= m)
		{
			sum += a[i];
		}
		else
		{
			ans++;
			sum = a[i];
		}
	}
	cout << ans << "\n";                      
	return 0;
}

回到正题(P1182)

使用P1181的大部分代码作为P1182的 check 函数

具体代码如下

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n, m;
int a[maxn];
bool check(int x)
{
	int cnt = 1;
	int sum = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (sum + a[i] <= x)
		{
			sum += a[i];
		}
		else
		{
			cnt++;
			sum = a[i];
		}
	}
	return cnt <= m;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int num = 0;
	int maxx = 0;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
		maxx = max(maxx, a[i]);
		num += a[i];
	}
	int l = maxx - 1;
	int r = num + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			r = mid;
		}
		else
		{
			l = mid;
		}
	}
	cout << r << "\n";
	return 0;
}
/*
5 3
4 2 4 5 1
*/

T4. B3629 吃冰棍

题目传送门

\(Wrong\) \(Code:\)

问题分析

做的时候想的是用模拟 \(+\) 枚举

题目说是二分

但是二分没有想到怎么做 😦

买的冰棍越多,剩余的木棍越多,

兑换的冰棍也会越多 冰棍的总数也会越多

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int n;
bool check(int x)
{
	int cnt = x;
	int sum = x;
	while (cnt >= 3)
	{
		int tmp = cnt / 3;
		cnt = cnt % 3;
		sum += tmp;
		cnt += tmp;
	}
	return sum >= n;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	int l = 1 - 1;
	int r = n + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			r = mid;
		}
		else
		{
			l = mid;
		}
	}
	cout << r << "\n";
	return 0;
}

T5. P2678 [NOIP2015 提高组] 跳石头

题目传送门

\(Wrong\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn];
int lenn, num, m;
bool check(int x)
{
	int res = a[1];
	int sum = 1;
	for (int i = 2; i <= num; ++i)
	{
		if (a[i] - res <= x)
		{
			res = a[i];
			sum++;
		}
	}
	if (lenn - a[num] <= x)
	{
		sum++;
	}
	if (a[1] - 1 <= x)
	{
		sum++;
	}
	return sum <= m;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> lenn >> num >> m;
	for (int i = 1; i <= num; ++i)
	{
		cin >> a[i];
	}
	int l = 0;
	int r = lenn + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	if (check(r))
	{
		cout << l << "\n";
	}
	else
	{
		cout << r << "\n";
	}
	return 0;
}

\(_{只过了样例和\#1}\) \(_{:(}\)

问题分析

最多移走块石头, \(l\) 代表两块石头的最近距离
如果两块石头之间的距离小于 \(l\) ,说明这块石头要被搬走
二分的答案就是 \(l\)\(l\) 越大,满足要求的石头更少

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 7;
int L, n, m;
int a[maxn];
bool check(int mid)
{
	int cnt = 0;
	int now = 0;
	for (int i = 1; i <= n; i++)
	{
		if (a[i] - a[now] < mid)
		{
			cnt++;
		}
		else
		{
			now = i;
		}
	}
	if (L - a[now] < mid)
	{
		cnt++;
	}
	return cnt <= m;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> L >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
	}
	int l = 0;
	int r = L + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	cout << l << "\n";
	return 0;
}

T6. P1843 奶牛晒衣服

\(_{题面有意思:)}\)

问题分析

用二分与 check 两个老朋友

但是 r 要足够大!!!

我用的是 \(5000 \times 5000 + 1\)

如果能够在 \(mid\) 时间内烘干.

将右区间缩小

注意: 要开 long long

\(AC\) \(Code:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5e5 + 7;
int w[maxn];
int n, a, b;
bool check(int x)
{
	int t = 0;
	for (int i = 1; i <= n; i++)
	{
		int tmp = w[i] - a * x;
		if (tmp > 0)
		{
			t += ceil(tmp * 1.0 / b);
		}
	}
	return t <= x;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> a >> b;
	for (int i = 1; i <= n; ++i)
	{
		cin >> w[i];
	}
	int l = 0;
	int r = 5000 * 5000 + 1;
	while (l + 1 < r)
	{
		int mid = (l + r) / 2;
		if (check(mid))
		{
			r = mid;
		}
		else
		{
			l = mid;
		}
	}
	cout << r << "\n";
	return 0;
}

\(\mathbb {THE\ END}\)

posted @ 2024-08-18 11:50  yucheng0630  阅读(14)  评论(1)    收藏  举报