2025蓝桥杯C++B组题解(口胡版)
填空题
1. 求最短路径
题目大概意思: 小明有两个操作, 往x轴方向走任意距离, 或者以(0,0)为圆心, 以自身到原点距离为半径, 走一段圆弧, 问到 (233, 666) 点最少需要走多少距离, 四舍五入保留整数
最短路径就是先往右走到 sqrt(233 * 233 + 666 * 666) , 然后走圆弧
x = 233.0, y = 666.0
dis = sqrt(x * x + y * y)
ans = dis + atan(y / x) * dis
cout << (int) (ans + 0.5)
答案好像是 157几
2. 2025排列
实验室大佬说:
1014后面的数字必须在自己的位置上, 前面的都可以偏移一个位置, 等价于用隔板法分隔 1013 个数字
答案是 2 1012 2^{1012} 21012 % mod
解答题
1. 特判题
题目大概意思: 称一个数 x x x (x >= 0)可以被构造, 为 x x x = sum(a, a+1, a+2 …),
sum(a, a+1, a+2 …)即一个公差为 1 的连续序列的和, a可以为任意整数, 该序列长度必须 >= 3
解题思路
除了1以外的所有数字都可以被构造出来
对于任意其他数字一定有 [-(x-1), -(x-2), … ,0, …, (x-2), (x-1), x ] 这样的序列使得使得sum = x 且序列长度 >= 3
int n; cin >> n;
int ans = 0;
for(int i = 1; i <= n; ++i) {
int t; cin >> t;
ans += (t != 1);
}
2. abck变变变
题目大概意思: 给三个数a,b,c 进行k次变换, 每次变换使 a = ⌊ b + c 2 ⌋ \lfloor \frac{b + c}{2} \rfloor ⌊2b+c⌋, b = ⌊ a + c 2 ⌋ \lfloor \frac{a + c}{2} \rfloor ⌊2a+c⌋ c = ⌊ a + b 2 ⌋ \lfloor \frac{a + b}{2} \rfloor ⌊2a+b⌋
解题思路: k >= 100 以后a b c 一定会变成相同的数字, 将题目给的 k 和 100 取小以后暴力即可
int a, b, c, k;
cin >> a >> b >> c >> k;
k = min(100, k)
...
3. 排序 + 双指针
题目大概意思: 有n个物品, 每个物品价值为 w i w_i wi, 要从中挑选 m 个物品, 使得 ∑ i = 2 m \sum_{i=2}^{m} ∑i=2m ( w [ i ] 2 − w [ i − 1 ] 2 ) \left( w[i]^2 - w[i-1]^2 \right) (w[i]2−w[i−1]2)最小
解题思路: 很明显, 对 n 个物品排序后, 选取的一定是某一个长度为 m 的连续序列, 那么直接双指针跑一边, 计算出所有长度为 m 的连续序列的值, 最后取小就行
int l = 0, r = 0, sum = 0;
int ans = 1e9 + 7;
while(l + m - 1 < n) {
while (r < l + m - 1) {
r += 1;
sum += w[r] * w[r] - w[r - 1] * w[r - 1];
}
ans = min(ans, sum);
l += 1
sum -= w[l] * w[l] - w[l - 1] * w[l -1]
}
4. dp
题目大概意思:
给定一个两行的字符串形如: …#…#.
##…
#代表一个净水器, 如果两个净水器在上下或者左右, 表示两个净水器联通, '.'可以理解成墙, 阻挡净水器联通, 如果净水器A和B联通, B和C联通, 则A和C联通
有一个操作可以将墙变成净水器, 问最少操作几次可以让所有净水器联通
解题思路:
- 首先形如
…##… |
---|
…##… |
这种数据, 我们要把头尾上下都为 ‘.’ 的东西去掉, 得到有效区间[l, r], 比如上面的有效区间就是[4, 5] (下标从1开始)
-
然后对有效区间 [l, r] 进行 dp
设 dp[0][i] 为第1个字符串的第i个字符为’#‘并且和前面联通, 最少需要几次操作
同理 dp[1][i] 为第2个字符串的第i个字符为’#'并且和前面联通, 最少需要几次操作转移大概如下, 应该是对的吧QwQ dp[0][i] = dp[0][i - 1] + (s[0][i] == '.') dp[1][i] = dp[1][i - 1] + (s[1][i] == '.') ta = min(dp[0][i], dp[1][i] + (s[0][i] == '.')) tb = min(dp[1][i], dp[0][i] + (s[1][i] == '.')) dp[0][i] = ta dp[1][i] = tb 最后答案就是 min(dp[0][r], dp[1][r])
5. 树上背包
题目大概意思: 有一个 根节点为1, n 个节点的树, 每一个点有一个 w i w_i wi 的权值
节点 | wi代表的意义 |
---|---|
根 | 单位时间可以 包装 多少货物 |
叶节点 | 单位时间可以 提供 多少原材料 |
其他 | 单位时间可以 改进 多少从上一个节点得到的东西 |
现在发现, 在原图上, 个别点单位时间得到的东西 > wi, 现在想删除一些点, 使得所有点 单位时间得到的东西 <= wi, 问最后单位时间, 根节点上最多能包装多少货物
n <= 1000, w <= 1000
解题思路: 假如只有根节点和若干叶子节点, 那么很明显是一个背包问题, 那么很容易类推到所有点, 对所有点跑书上背包即可
优化: 用 bitset 来存储dp状态, 转移更快, 空间更小
6. 异或和
题目大概意思:
给定一个序列 如 1 3 5 7 9, 在数字与数字之间插入 ^, +, 或 - 运算符, ^优先计算, ±其次,
问所有可能的情况的值的和是多少(对1e9 取模)
解题思路:
设 sum(l, r) 为 a l a_l al ^ a l + 1 a_{l+1} al+1^ … ^ a r a_r ar, 可以发现任何 l $\neq $ 1 的段, 对最终的答案没有贡献,
比如一定有 1 + (3 ^ 5 ^ 7 ^ 9) 和 1 - (3 ^ 5 ^ 7 ^ 9) 这种序列, 使得 (3 ^ 5 ^ 7 ^ 9) 对最终的答案贡献为零
那么只需要计算 l l l 为 1的所有前缀异或的贡献即可
贡献等于后面操作为 + -, 此外的操作任意, 比如 (1 ^ 3 ^ 5) (+ or -) 7 (+ or - or ^) 9
那么 (1 ^ 3 ^ 5) 的贡献就是 (1 ^ 3 ^ 5) * 2 * 3 1 3^1 31
// 大概是这样
// p3[i] 为 3^i 对mod的取模, pre[i] 为 1 ~ i 的前缀异或大小
ans = pre[n] // 1 - n
for(int i = 1; i < n; ++i) { // 1 - (n - 1)
ans += pre[i] * 2 * p3[n - 1 - 1 - (r - 1)];
ans = ans % mod;
}