2025牛客寒假算法基础集训营5
A
略
B
贴个代码就完了
CODE
点击查看代码
void solve()
{
int n = 0, t = 0, k = 0;
std::cin >> n >> t >> k;
int ans = (n - k) / t;
if (ans - 1 >= k) {
ans = k + 1;
}
std::cout << ans << '\n';
}
C
题目大意
以 01 串的形式给出 3 个数
- 以
的代价反转一位 - 在一个数中选两位以
的代价交换这两位
问至少需要花费多少代价才能使得
解题思路
首先容易想到当
可以把所有不合法的位分为 4 类:
- 0:在该位上
为 0, 为 0; - 1:在该位上
为 0, 为 1; - 2:在该位上
为 1, 为 0; - 3:在该位上
为 1, 为 1;
由于对于每个不和法的位,我们只需要在两个数中任选一个来反转该位就能使该位合法,所以上述 4 类不合法的位(包含了所有不合法的位),能够选择其他 3 类来进行操作 2。比如第 0 类和第 1 类就在
所以问题又变成了有 4 种颜色的小球,不同颜色的小球间可以两两配对,求最多能配多少对。
这种问题不管有几种颜色,解法都是统一的,即先看数量最多的球是否超过到总数的一半,超过就其他的球都和它配对,最后还有剩的就不能再配对了。否则配对数直接就是总数除 2 向下取整,如果总数是奇数最后就会剩下一个球。
知道了能进行多少次交换操作后,剩下的就都是操作 1 了。
CODE
点击查看代码
void solve()
{
i64 n = 0, x = 0, y = 0;
std::cin >> n >> x >> y;
std::string a, b, c;
std::cin >> a >> b >> c;
std::vector cnt(4, 0ll);
for (int i = 0; i < n; i++) {
if (c[i] == '0') {
if (a[i] == '0' && b[i] == '1') {
cnt[1]++;
}
else if (a[i] == '1' && b[i] == '0') {
cnt[2]++;
}
}
else {
if (a[i] == '0' && b[i] == '0') {
cnt[0]++;
}
else if (a[i] == '1' && b[i] == '1') {
cnt[3]++;
}
}
}
i64 sum = cnt[0] + cnt[1] + cnt[2] + cnt[3];
if (x * 2 <= y) {
std::cout << x * sum << '\n';
}
else {
std::sort(cnt.begin(), cnt.end());
if (cnt[3] * 2 >= sum) {
i64 num = sum - cnt[3];
std::cout << y * num + x * (cnt[3] - num) << '\n';
}
else {
std::cout << y * (sum / 2) + x * (sum % 2) << '\n';
}
}
return;
}
D
题目大意
在 01 数组
- 将段内所有的数 01 互换。
- 任意排列段内的数。
将操作后得到的序列中所有连续相同的数都变成一个数后得到一个 01 相间的序列,我们定义
求
解题思路
根据题目描述的操作,我们要知道如果某一段中有若干数量大于 0 的 0 和 1,我们就可以把这段看成 10
,如果某一段中只有一个数,不管是 0 还是 1 我们都可以把这段看成 00
。于是接下来我们考虑,10
拼成的序列通过合并连续相同的数最短能达到多长。答案是 100110
)。再考虑如果 10
变成了 00
,能达到的最短长度是多少。答案是
于是我们就可以这么来处理:先令 10
。然后再去计算每段长度为
CODE
点击查看代码
void solve()
{
int n = 0;
std::string s;
std::cin >> n;
std::cin >> s;
std::vector ans(n + 1, 1);
for (int i = 1; i <= n; i++) {
ans[i] += (n + i - 1) / i; // n / i 向上取整
}
for (int l = 0, r = 0; l < n; l = r) {
while (r < n && s[l] == s[r]) {
r++;
}
int len = r - l;
for (int k = 1; k <= len; k++) {
int ll = (l + k - 1) / k * k;
ans[k] -= (r - ll) / k;
if (r == n && (r - ll) % k != 0) {
ans[k]--;
}
}
if (r == n) {
for (int k = len + 1; k <= n; k++) {
if ((l + k - 1) / k * k < r) {
ans[k]--;
}
}
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
res ^= ans[i];
}
std::cout << res << '\n';
}
E
题目大意
给定一个未分出胜负的三子棋局面,且双方走了相同的步数。问让白子先走两步白子能否必胜。
解题思路
当各走走了 2 步以下时,白子是必胜的。
当各走了 4 步时,由于只剩下一个位置,所以下完检查以下就好了。
当各走了 3 步,还剩下 3 个位置,
CODE
点击查看代码
bool chk() {
for (int i = 0; i < 3; i++) {
if (g[i] == "XXX") {
return true;
}
}
for (int j = 0; j < 3; j++) {
bool line = true;
for (int i = 0; i < 3; i++) {
if (g[i][j] != 'X') {
line = false;
break;
}
}
if (line) {
return true;
}
}
if (g[1][1] == 'X' && g[0][0] == 'X' && g[2][2] == 'X') {
return true;
}
if (g[1][1] == 'X' && g[0][2] == 'X' && g[2][0] == 'X') {
return true;
}
return false;
}
void chg(std::array<int, 2> p, char c) {
g[p[0]][p[1]] = c;
}
bool solve()
{
int cnt = 0;
std::vector<std::array<int, 2>> pos;
for (int i = 0; i < 3; i++) {
std::cin >> g[i];
for (int j = 0; j < 3; j++) {
if (g[i][j] == 'X') {
cnt++;
}
else if (g[i][j] == 'G') {
pos.push_back({ i, j });
}
}
}
if (cnt <= 2) {
return true;
}
else if (cnt == 4) {
chg(pos[0], 'X');
return chk();
}
else {
for (int i = 0; i < 3; i++) {
chg(pos[i], 'X'), chg(pos[(i + 1) % 3], 'X');
if (chk()) {
return true;
}
chg(pos[i], 'G'), chg(pos[(i + 1) % 3], 'G');
}
}
return false;
}
H
题目大意
将一个长度为
解题思路
这种题一般是要单独算贡献。所以直接枚举所有区间,再看有多少划分方案是包含这个区间的就好了。用隔板法考虑,注意下当枚举到的区间是最左边或者最右边的情况。
CODE
点击查看代码
void solve()
{
int n = 0, k = 0;
std::cin >> n >> k;
C[0][0] = 1;
for (int i = 1; i <= n; i++) {
C[i][0] = 1;
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
}
}
std::vector st(n, std::vector(L, std::array<i64, 2>{ 0, 0 }));
for (int i = 0; i < n; i++) {
std::cin >> st[i][0][0];
st[i][0][1] = st[i][0][0];
}
for (int l = 1; l < L; l++) {
for (int i = 0; i + (1 << l) <= n; i++) {
st[i][l][0] = std::max(st[i][l - 1][0], st[i + (1 << l - 1)][l - 1][0]);
st[i][l][1] = std::min(st[i][l - 1][1], st[i + (1 << l - 1)][l - 1][1]);
}
}
// 左闭右开
auto quiry = [&](int l, int r) -> i64 {
int len = log2(r - l);
int m = r - (1 << len);
return std::max(st[l][len][0], st[m][len][0]) * std::min(st[l][len][1], st[m][len][1]) % Mod;
};
i64 ans = 0;
if (k == 1) {
ans = quiry(0, n);
}
else if (k == 2) {
for (int i = 1; i < n; i++) {
(ans += (quiry(0, i) + quiry(i, n)) % Mod) %= Mod;
}
}
else {
for (int len = 1; len + k - 1<= n; len++) {
for (int i = 0; i + len <= n; i++) {
int d = (i == 0 || i + len == n) ? 1 : 2;
(ans += quiry(i, i + len) * C[n - len - d][k - 1 - d] % Mod) %= Mod;
}
}
}
std::cout << ans << '\n';
return;
}
I
相等判 YES,否则看有没有 0。
CODE
点击查看代码
bool solve()
{
int n = 0, m = 0;
std::cin >> n >> m;
if (n == m) {
return true;
}
else if (n == 0 || m == 0) {
return false;
}
else {
return true;
}
}
J
题目怎么说就怎么做
CODE
点击查看代码
void solve()
{
int n = 0;
std::string s;
std::cin >> n >> s;
i64 ans = 0;
int v = 0;
for (auto &c : s) {
if (c == '0') {
v += 10;
ans += v;
}
else if (c == '1') {
v = std::max(0, v - 5);
ans += v;
}
else {
ans += std::max(0, v - 10);
}
}
std::cout << ans << '\n';
}
K
题目大意
在平面上有若干个点,点
解题思路
除了横或纵坐标相同的点外,还需要赵勾股数满足
(感觉找勾股数可以单独写一篇博客)
CODE
点击查看代码
constexpr int N = 1e5, M = 2e5, Inf = 1e9, MXR = 3e5;
std::vector<std::array<int, 2>> R[MXR + 5];
std::unordered_set<int> mp[M + 5];
int ans;
void add(int x, int y) {
if (x >= 0 && y >=0 && x <= M && y <= M && mp[x].count(y) != 0) {
ans++;
}
}
void solve()
{
for (int i = 1; i * i <= MXR; i++) {
for (int j = 1; j < i && i * i + j * j <= MXR; j++) {
if ((i + j) % 2 == 0 || std::__gcd(i, j) != 1) {
continue;
}
int a = 2 * i * j;
int b = i * i - j * j;
int c = i * i + j * j;
for (int k = 1; c * k <= MXR; k++) {
R[c * k].push_back({ a * k, b * k });
}
}
}
int n = 0;
std::cin >> n;
std::vector p(n, std::array<int, 3>{});
for (auto &[x, y, r] : p) {
std::cin >> x >> y >> r;
x += N, y += N;
mp[x].insert(y);
}
for (auto &[x, y, r] : p) {
if (r > MXR) {
continue;
}
add(x + r, y);
add(x - r, y);
add(x, y + r);
add(x, y - r);
for (auto &[d1, d2] : R[r]) {
add(x + d1, y + d2);
add(x + d1, y - d2);
add(x - d1, y + d2);
add(x - d1, y - d2);
add(x + d2, y + d1);
add(x + d2, y - d1);
add(x - d2, y + d1);
add(x - d2, y - d1);
}
}
std::cout << ans << '\n';
}
L
构造。
我观察到的是从 1 开始以数 6 个为一组,每组可以产生两个符合条件的三元组。
当
CODE
点击查看代码
void solve()
{
int n = 0;
std::cin >> n;
int ans = n / 6;
if (n <= 3) {
std::cout << 0 << '\n';
}
else if (n % 6 == 3) {
std::cout << ans * 2 + 1 << '\n';
std::cout << 1 << ' ' << 3 << ' ' << n << '\n';
std::cout << 2 << ' ' << 4 << ' ' << 5 << '\n';
std::cout << n - 1 << ' ' << n - 2 << ' ' << n - 3 << '\n';
for (int i = 6; i < n - 3; i += 6) {
std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
std::cout << i + 2 << ' ' << i + 4 << ' ' << i + 5 << '\n';
}
}
else {
int d = 0;
if (n % 6 >= 4) {
d = 1;
}
std::cout << ans * 2 + d << '\n';
for (int i = 1; i < ans * 6; i += 6) {
std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
std::cout << i + 2 << ' ' << i + 4 << ' ' << i + 5 << '\n';
}
if (d == 1) {
int i = ans * 6 + 1;
std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
}
}
return;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具