2025牛客寒假算法基础集训营5

2025牛客寒假算法基础集训营5

小L的三则运算

思路

签到。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	i64 x;
	char c;
	cin >> x >> c;

	if (c == '*') {
		cout << x << " " << 1 << "\n";
	} else if (c == '+') {
		cout << x - 1 << " " << 1 << "\n";
	} else {
		cout << x + 1 << " " << 1 << "\n";
	}

	return 0;
}

小L出师了

思路

注意要和 \(k+1\) 即分开的段数取最小值。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n, k, t;
	cin >> n >> t >> k;

	cout << min((n - k) / t, k + 1) << "\n";

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

小L的位运算

思路

来自出题人题解。

有关贪心的小思维题。

由于反置代价小于等于交换代价,那么一定不能用交换去代替反置。

首先,如果交换的代价大于两次反置的代价,那么直接全部使用反置即可。

否则,我们将不匹配的位置分类,发现最多只有四类,\(ab\) 分别是 \(00\), \(01\), \(10\), \(11\),任意两个不同种类不匹配的话,我们一定可以交换其中的某一位 \(0\)\(1\) 使之两两匹配,那么我们就只看最多的一类不匹配的位置的数量有没有超过总数的一半即可。

如果超过就超过的部分反置,其余匹配,否则一定存在一种分配方式使之匹配后至多仅剩一个。

循环枚举一下即可,复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, x, y;
	cin >> n >> x >> y;

	string a, b, c;
	cin >> a >> b >> c;

	i64 has = 0;
	vector cnt(2, vector<i64>(2));
	for (int i = 0; i < n; i ++) {
		if ((a[i]^b[i]) + '0' != c[i]) {
			cnt[a[i] - '0'][b[i] - '0']++;
			has ++;
		}
	}

	i64 Max = 0;
	for (int i : {0, 1}) {
		for (int j : {0, 1}) {
			Max = max(Max, cnt[i][j]);
		}
	}

	y = min(y, 2 * x);
	i64 m = has / 2;
	if (Max * 2 > has) {
		m = has - Max;
	}
	cout << m * y + (has - 2 * m) * x << "\n";

	return 0;
}

小L的字符串翻转

思路

有关贡献的小思维题。

出于个人习惯,我们将数字称呼为颜色。

如果对于每个 \(k\) 单独考虑。注意到最后答案的段数等于颜色段分界的数量加一,而只有在分段后存在两种不同颜色的段才会有颜色段的分界,那么其实答案就是存在两种不同颜色的段的数量加一。预处理一下 \(0\)\(1\) 的前缀和,再调和级数枚举一下可以 \(O(n \log n)\) 通过此题。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	string s;
	cin >> s;

	s = " " + s;
	vector<array<int, 2>> cnt(n + 1);
	for (int i = 1; i <= n; i ++) {
		cnt[i] = cnt[i - 1];
		cnt[i][s[i] - '0'] ++;
	}

	i64 ans = 0 ;
	for (int i = 1; i <= n; i ++) {
		int res = 1;
		for (int j = 1; j <= n; j += i) {
			int c[2] {};
			for (int k : {0, 1}) {
				c[k] = cnt[min(j + i - 1, n)][k] - cnt[j - 1][k];
			}
			res += (c[0] && c[1]);
		}
		ans ^= res;
	}

	cout << ans << "\n";

	return 0;
}

小L的井字棋

思路

披着搜索外壳的分类讨论题。

你可以使用记忆化搜索去做,码量比较大,复杂度比较玄学,不同的搜索方式不一样,有部分这样写的代码可以通过,这里不再赘述。

再往下想想,其实我们可以发现先手下的人赢面本来就很大。

首先如果棋盘上没有棋子,那么我们下中间,然后再使用特权随便下两个可以连起来的地方,即可胜利。

如果棋盘上已经各有一颗棋子,那么这颗棋子一定至少有一个方向没有对方的棋子,我可以直接使用特权胜利。

如果棋盘上已经各有四颗棋子,直接填人后判断即可。

如果棋盘上已经各有三颗棋子,那么一定是使用特权更优,三种方式枚举一下,结果或起来即可。

如果棋盘上已经各有两颗棋子,手算枚举一下各个情况,发现无论怎么下都是先手必胜的,直接Yes 即可。

按照上面的情况即可通过此题,复杂度为 check 函数的复杂度,大概为几十的常数。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {


	vector<string> s(3);
	vector<int> a(3), b(3);

	int cnt = 0, l = 0, r = 0;
	for (int i = 0; i <= 2; i++) {
		for (int j = 0; j <= 2; j++) {
			cin >> s[i][j];
			if (s[i][j] == 'G')cnt++;
			if (s[i][j] == 'O') {
				a[i] = 1, b[j] = 1;
				if (i == j)l = 1;
				if (i + j == 2)r = 1;
			}
		}
	}
	if (cnt == 9 || cnt == 7 || cnt == 5) {
		cout << "Yes\n";
		return;
	}
	
	for (int i = 0; i <= 2; i++) {
		for (int j = 0; j <= 2; j++) {
			if (s[i][j] == 'X') {
				if (a[i] == 0 || b[j] == 0 || (i == j && l == 0) || (((i + j) == 2) && r == 0)) {
					cout << "Yes\n";
					return;
				}
			}
		}
	}
	cout << "No\n";

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

小L的数学题

思路

猜猜题,不会证,给出出题人证明。

\(n\)\(m\) 中仅有一个是 0,就是 \(No\)

如果都是 0,就是 \(Yes\)

否则,大于 0 的正整数是一定可以互相转换的,证明如下:

首先,任何正整数都能达到 1。

考虑一个数 \(k^{2^j} < x < (k+1)^{2^j}\),那么 \(x\)\(j\) 次 sqrt 就可以得到 \(k\)

两边取 \(log_2\)\(j\) 足够大的时候 \(2^j(log_2(k+1)-log_2(k)) > 1\),所以一定有整数 \(i\)\(2^jlog_2(k)\)\(2^jlog_2(k+1)\) 之间。

\(x = 2^i\)

即可得到 \(k\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n, m;
	cin >> n >> m;

	if (!m || !n) {
		cout << (n == m ? "Yes\n" : "No\n");
		return;
	}

	cout << "Yes\n";

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

小L的汽车行驶问题

思路

按照题目要求维护一个速度,然后每秒钟加上速度\(u\)的值就行了。
油门就加\(10\) ,刹车就减\(5\),离合就不便,只是加上的速度是\(\max(v- 10,0)\)。复杂度\(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	string s;
	cin >> s;

	int v = 0;
	i64 ans = 0;

	for (auto c : s) {
		auto t = v;
		if (c == '0') {
			v += 10;
		} else if (c == '1') {
			v -= 5;
		} else {
			v -= 10;
		}
		v = max(0, v);
		ans += v;
		if (c == '2') {
			v = t;
		}
	}

	cout << ans << "\n";

	return 0;
}

小L的构造

思路

\(n \leq 3\) 时,无解。

\(n > 3\) 时候,分 \(mod\) 6 考虑。

首先 \(p = n/3\)

模 6 余小于等于 2 时,直接连续六个一组,设最小的为 \(x\),那么 \(x, x+1, x+3\)\(x+2, x+4, x+5\) 一定符合条件,因为 \(x+3\)\(x+1\) 都是偶数,不互质,\(x\)\(x+1\) 互质,\(x+3\)\(x\) 相差 3,但它们都不是 3 的倍数,故互质。\(x+2\)\(x+5\) 都是 3 的倍数,故不互质,\(x+4\)\(x+5\) 互质,\(x+4\)\(x+2\) 是相邻的奇数,互质。故符合条件。

大于等于 3 时,先把前 9 个数拿出来匹配,前 9 个可以配成\(1,2,4;3,9,5;6,8,7\),后面形如上面匹配即可。

复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n;
	cin >> n;

	int	ans = n / 3;
	if (n <= 3) {
		ans = 0;
	}

	cout << ans << "\n";

	for (int i = 0; i < ans; i++) {
		if (n % 6 == 3 && i == ans - 2) {
			cout << i * 3 << ' ' << 2 + i * 3 << ' ' << 6 + i * 3 << "\n";
		} else if (n % 6 == 3 && i == ans - 1) {
			cout << 1 + i * 3 << ' ' << 2 + i * 3 << ' ' << i * 3 << "\n";
		} else if (i % 2 == 0) {
			cout << 1 + i * 3 << ' ' << 2 + i * 3 << ' ' << 4 + i * 3 << "\n";
		} else {
			cout << i * 3 << ' ' << 2 + i * 3 << ' ' << 3 + i * 3 << "\n";
		}
	}

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}
posted @ 2025-02-09 19:26  Ke_scholar  阅读(25)  评论(0)    收藏  举报