1481 字
4 分钟
2026第五&六次蓝桥赛制个人赛题解

想了想后发现,由于水题不想写,难题不会做,每场比赛大概只有 2 题值得我去写题解,还是每两次比赛发一次题解比较好

5-D. Subarray Sum (通过人数 5/56)#

题目描述#

构造一个长度为 NN 的整数序列 AA,满足 1Ai1091 \le A_i \le 10^9,且恰好有 KK 个子段的和等于 SS

输入格式#

输入通过标准输入给出,格式如下:

N K SN \ K \ S

  • 1N1051 \le N \le 10^5
  • 0KN0 \le K \le N
  • 1S1091 \le S \le 10^9
  • 输入的所有值均为整数。

题解#

由于评测姬坏掉导致 SPJ 的判定问题导致通过人数少的可怜,实际上这个题目非常简单。

简单构造题。想让构造方法尽可能简单,我们直接让符合条件的子段和为这个数 SS 本身就好了,想要 KKSS 我们就先直接输出 KKSS

由于一共要输出 NN 个数,那我们只需要保证输出的数列不可能凑出一个子段和为 SS 即可,自然想到填充比 SS 大的数,比如 S+1S+1 即可。此处需要特判一下,由于要求输出的数 Ai109A_i \le 10^9 ,如果 SS 恰好为 10910^9 ,我们不妨输出 11 ,毕竟不可能有 10910^911 让你凑出 SS

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
void work() {
int n, k, s;
cin >> n >> k >> s;
for (int i = 0; i < n; ++i) {
if (i < k) {
cout << s;
} else {
if (s == 1000000000) {
cout << 1;
} else {
cout << s + 1;
}
}
if (i != n - 1) cout << ' ';
}
cout << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
work();
}
return 0;
}

6-D. Favorite Number (通过人数 24/50)#

题目描述#

给定正整数 NN,求所有满足 Nm=(Nmodm)\lfloor \frac{N}{m} \rfloor = (N \mod m) 的正整数 mm 的和。

输入格式#

一个数 NN 满足 1N10121 \le N \le 10^{12}

题解#

一眼根号时间复杂度 由于 N1012N \le 10^{12},直接枚举 mm 肯定超时,我们需要寻找更优的做法。

光看条件会有点不知从何下手,不妨列方程看看。设 Nm=(Nmodm)=x\lfloor \frac{N}{m} \rfloor = (N \mod m) = x,则有 N=mx+x=x(m+1)N = m \cdot x + x = x(m+1),移项可得 m=Nx1m = \frac{N}{x} - 1

由于 mm 是正整数,且 xx 是余数,必须满足 0x<m0 \le x < m。 代入 mm 的表达式:x<Nx1    x(x+1)<Nx < \frac{N}{x} - 1 \implies x(x+1) < N

因此,我们只需要枚举满足 x(x+1)<Nx(x+1) < N 的正整数 xx。如果 xx 能整除 NN,则对应的 m=Nx1m = \frac{N}{x} - 1 就是一个合法的解。

时间复杂度为 O(N)O(\sqrt{N})

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define rep(i, f, l) for(int i(f); i <= l; ++i)
#define per(i, f, l) for(int i(f); i >= l; --i)
const int N = 2e5 + 5;
void work() {
int n;
cin >> n;
int ans = 0;
for(int x = 1; x * (x + 1) < n; x++){
if(n % x) continue;
ans += (n / x) - 1;
}
cout << ans << endl;
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
work();
}
return 0;
}

6-D. Concatenation (通过人数 19/50)#

题目描述#

给定 NN 个字符串 s1,s2,,sNs_1, s_2, \dots, s_N。 将这些字符串任意排序后拼接成一个长字符串,求拼接后的字符串中 “AB” 出现次数的最大值。

输入格式#

NN s1s_1sNs_N

  • 1N1041 \le N \le 10^4
  • 2si102 \le |s_i| \le 10
  • sis_i 仅由大写英文字母组成。

题解#

首先统计每个字符串内部的 “AB” 数量。

然后考虑拼接处的 “AB”,这取决于前一个串的末尾是否为 ‘A’ 且后一个串的开头是否为 ‘B’。 我们将字符串按首尾字符分类:

  • x类:B... (首 B 尾非 A),个数为 xx
  • y类:...A (首非 B 尾 A),个数为 yy
  • z类:B...A (首 B 尾 A), 个数为 zz

贪心策略:

  1. z 类字符串可以全部串联,贡献 z1z-1 个 “AB”,并形成一个巨大的 B...A 串。
  2. 若同时存在 xy 类,可将 z 串联后的整体夹在中间:(...A) + [B...A] + (B...),额外贡献 2 个 “AB”。
  3. 剩余的 xy 两两配对。

根据 x,y,zx, y, z 是否存在分类讨论输出即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define rep(i, f, l) for(int i(f); i <= l; ++i)
#define per(i, f, l) for(int i(f); i >= l; --i)
const int N = 1e4 + 5;
string s[N];
void work() {
int n;
cin >> n;
int ans = 0;
rep(i, 1, n) cin >> s[i];
int x = 0, y = 0, z = 0;
rep(i, 1, n){
for(int j = 0; j < s[i].size() - 1; j++){
if(s[i][j] == 'A' && s[i][j + 1] == 'B'){
ans++;
j++;
}
}
if(s[i][0] == 'B' && s[i][s[i].size() - 1] == 'A'){
z++;
} else if(s[i][0] == 'B'){
x++;
} else if(s[i][s[i].size() - 1] == 'A'){
y++;
}
}
if(x && y && z){
cout << ans + z + 1 + min(x - 1, y - 1) << endl;
} else if(x && y){
cout << ans + min(x, y) << endl;
} else if((x && z) || (y && z)){
cout << ans + z << endl;
} else if(x == 0 && y == 0 && z > 0){
cout << ans + z - 1 << endl;
} else {
cout << ans << endl;
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
work();
}
return 0;
}

5-A. Swap and Flip (通过人数 8/56)#

题目描述#

NN 张卡片,编号为 1,2,,N1, 2, \dots, N。 第 ii 张卡片 (1iN1 \le i \le N) 正面写有整数 AiA_i,背面写有整数 BiB_i。 初始时,卡片按 11NN 的顺序从左到右排列,正面朝上。 判断是否可以通过以下操作使朝上的数字从左到右构成非降序列。如果可以,求最小操作次数。

  • 选择整数 ii (1iN11 \le i \le N-1)。交换第 ii 张和第 i+1i+1 张卡片,并翻转这两张卡片。

输入格式#

NN A1 A2  ANA_1 \ A_2 \ \dots \ A_N B1 B2  BNB_1 \ B_2 \ \dots \ B_N

  • 1N181 \le N \le 18
  • 1Ai,Bi501 \le A_i, B_i \le 50

题解#

看到数据范围,是不是应该 0秒猜出算法

卡牌个数 N18N \le 18,表明我们可以二进制暴力枚举每个卡牌是否被翻转;而只要每张卡牌确定是否被翻转,排序后的卡牌顺序也可以唯一确定了。注意这里和之后的 “被翻转” 是指最终状态而不考虑过程。

想到这,我们不妨思考一下:每种二进制状态都一定是合法的吗?我们模拟几组数据就可以不难发现:

  • 被翻转的卡牌个数一定是偶数个。
  • 假设一张卡牌现在位于 xx,被排序后位于 yy,那么若 xy0(mod2)|x-y| \equiv 0 \pmod 2,这张卡牌一定没有被翻转;若 xy1(mod2)|x-y| \equiv 1 \pmod 2,则一定被翻转。因为卡牌每移动一格就要翻一次,所以也很好理解。

那我们先检验上述约束的合法性。对于第一个,假设我们二进制枚举时用 11 表示卡牌被翻转了,只需要统计状态 11 的个数就好了。如果 11 的个数是奇数直接不考虑这个状态。

对于第二个约束,既然确定了每个卡片的状态,每个卡的值也是确定的。我们把这些卡牌的值排序后保存在一个 targ 数组中,表示我们的目标状态。接着我们暴力循环一一比对targ 的每张卡和原数组的每张卡,再根据约束检验合法性就好了。如果不合法,我们就跳过当前这个比对循环。

如果这些约束都满足,那我们如何计算操作次数呢?由于我们只能交换相邻元素,将一个序列变为有序序列的最小交换次数等于该序列的逆序对数。具体地,若排序后的第 ii 个位置存放的是原序列中第 pip_i 个位置的卡片,那么 p0,p1,,pN1p_0, p_1, \dots, p_{N-1} 这个排列的逆序对数就是当前状态下的最小操作次数。

二进制枚举时间复杂度 O(2N)O(2^N),比对序列以及计算逆序对时间复杂度都是 O(N2)O(N^2),因此总的时间复杂度为 O(N22N)O(N^2 \cdot 2^N)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define rep(i, f, l) for(int i(f); i <= l; ++i)
#define per(i, f, l) for(int i(f); i >= l; --i)
const int N = 30;
int a[N];
int b[N];
int pos[N];
int vis[N];
int ans = INT_MAX;
struct node{
int x;
int state;
};
node nums[N];
void work() {
int n;
cin >> n;
rep(i, 0, n - 1) cin >> a[i];
rep(i, 0, n - 1) cin >> b[i];
for(int s = 0; s <= (1 << n) - 1; s++){
if (__builtin_popcount(s) % 2 != 0) continue;
vector<int> targ;
for(int i = 0; i < n; i++){
if(s & (1 << i)){
nums[i].x = b[i];
nums[i].state = 1;
} else{
nums[i].x = a[i];
nums[i].state = 0;
}
targ.push_back(nums[i].x);
}
sort(targ.begin(), targ.end());
bool fail = 0;
for(int i = 0; i < n; i++) vis[i] = 0;
for(int i = 0; i < n; i++){
bool f = 0;
for(int j = 0; j < n; j++){
if(vis[j]) continue;
if(nums[i].x != targ[j]) continue;
if(abs(i - j) % 2 != nums[i].state) continue;
if(nums[i].x < targ[j]) break;
vis[j] = 1;
pos[i] = j;
f = 1;
break;
}
if(!f){
fail = 1;
break;
}
}
if(!fail){
int cnt = 0;
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
if(pos[i] > pos[j]) cnt++;
}
}
ans = min(ans, cnt);
}
}
if(ans == INT_MAX) cout << -1 << endl;
else cout << ans << endl;
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
work();
}
return 0;
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

2026第五&六次蓝桥赛制个人赛题解
https://blog.kyy008.me/posts/26-寒训56/
作者
Kyy008
发布于
2026-02-11
许可协议
CC BY-NC-SA 4.0