2025年 蓝桥杯C/C++B组省赛 个人代码
第十六届蓝桥杯C/C++B组省赛个人代码
A. 移动距离
- 最后做的填空,凭感觉猜的。
- 先往右走欧几里得距离,再往上转到目标点。
- 证明需要画图,这里就不写了。
- 不知道有反三角函数,求弧长用的实数域二分答案求角度占比。
B. 客流量上限
不会,第二个填空从来没做出来过。
C. 可分解的正整数
赛时看一眼填空不太好做,直奔C题。
没认真看题思考,只取了3的倍数(序列长度是3的),毫无疑问WA了
解题思路
- 对于一个正整数\(n\),可以写成\(\sum_{i=-n+1}^{n}i\)。
- \(n=1\)时,序列长度只为2。
- 所以除了\(1\)都可以。
D. 产值调整
赛时直接猜,当操作次数非常多的时候,三个数会趋向于相等。
最大操作次数是\(log\)级别的,实在不会就打表,最多几十次就会相等。
AC代码
#include<bits/stdc++.h>
void Main() {
int a, b, c, k;
std::cin >> a >> b >> c >> k;
while (k--) {
int x = b + c >> 1;
int y = a + c >> 1;
int z = a + b >> 1;
a = x, b = y, c = z;
if (a == b && a == c) {
break;
}
}
std::cout << a << " " << b << " " << c << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) {
Main();
}
return 0;
}
E. 画展布置
解题思路
因为可以随便取\(m\)个,并打乱顺序,所以尽量取数值上较为集中的\(m\)个数。
先排个序,维护个前缀和,然后长度\(m\)的滑动窗口,取连续的\(m\)个即可。
- 时间复杂度\(O(nlogn)\)。
AC代码
#include<bits/stdc++.h>
using i64 = long long;
void Main() {
int n, m;
std::cin >> n >> m;
std::vector<i64> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
a[i] *= a[i];
}
sort(a.begin() + 1, a.end());
std::vector<i64> pre(n + 1);
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + a[i] - a[i - 1];
}
i64 ans = 1e18;
for (int i = m; i <= n; i++) {
ans = std::min(ans, pre[i] - pre[i - m + 1]);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
F. 水质检测
赛时贪心漏情况了,没考虑全面,WA一半。
解题思路
- 最初思路肯定是把所有联通块挑出来,相邻的联通块连接。
- 为了方便,把竖着两个字符变成了二进制数。'#'表示\(1\),'.'表示\(0\)。
- 时间复杂度\(O(n)\)。
赛时错误代码
#include<bits/stdc++.h>
void Main() {
std::string s[2];
std::cin >> s[0] >> s[1];
std::vector<std::pair<int, int>> piece;
for (int i = 0; i < s[0].size(); i++) {
int t = 0;
for (int j = 0; j < 2; j++) {
if (s[j][i] == '#') {
t += 1 << (1 - j);
}
}
if (t) {
piece.push_back({i, t});
}
}
int ans = 0;
for (int i = 1; i < piece.size(); i++) {
auto &[l, v1] = piece[i - 1];
auto &[r, v2] = piece[i];
if (r - l == 1 && v1 & v2) {
continue;
}
if (v1 == 3 || v2 == 3 || v1 == v2) {
ans += r - l - 1;
} else {
ans += r - l;
// v2 = 3;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
AC代码
#include<bits/stdc++.h>
void Main() {
std::string s[2];
std::cin >> s[0] >> s[1];
std::vector<std::pair<int, int>> piece;
for (int i = 0; i < s[0].size(); i++) {
int t = 0;
for (int j = 0; j < 2; j++) {
if (s[j][i] == '#') {
t += 1 << (1 - j);
}
}
if (t) {
piece.push_back({i, t});
}
}
int ans = 0;
for (int i = 1; i < piece.size(); i++) {
auto &[l, v1] = piece[i - 1];
auto &[r, v2] = piece[i];
if (r - l == 1 && v1 & v2) {
continue;
}
if (v1 == 3 || v2 == 3 || v1 == v2) {
ans += r - l - 1;
} else {
ans += r - l;
v2 = 3;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
区别就是else里是否对\(v2\)进行赋值\(3\)。
对于两个联通块连接处一上一下(一个\(1\),一个\(2\))的情况,比如\(21\),可以变成\(23\)或者\(31\)。
但是对于连着三个连通块上下上或者下上下的情况,比如\(212\),变成\(232\)是最优的,而变成\(312\),则再需要一个变成\(332\)才能联通三个。
所以当上述所述情况时,尽量选择让后者变成\(3\)(两个'#'),这样就有可能在联通第三个联通块时少用一个。
G. 生产车间
赛时没做数据,过样例直接交了,估计WA一半。
解题思路
- 树上背包
- 从最底层往上,用\(u\)节点的子节点对\(u\)进行背包。
- dp[i][j]表示节点\(i\)能否获得\(j\)的价值。
- 为什么要从下往上,因为要使用子节点对父节点更新信息时,你要保证子节点的信息已经全部更新完了。
- 整体过程直接DFS,回溯后就可以保证已经访问并更新了所有子节点,再对当前节点dp。我赛时还记录了深度,按层来,完全按照由下到上,有点蠢了。
- 时间复杂度感觉是\(O(n^3)\),不知道为啥能跑这么快。
- 好像可以\(bitset\)优化,也有人用FFT。(暂时不会)
赛时错误代码+错误指出(就差一点,没考虑到根节点也有可能度是1,被迫当成叶子了)
#include<bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
int n;
const int N = 1e3 + 2;
int d[N], fa[N], dp[N][N], a[N];
vector<vector<int>> e(N), D(N);
void dfs(int x, int f) {
fa[x] = f;
d[x] = d[f] + 1;
for (auto u : e[x]) {
if (u != f) {
dfs(u, x);
}
}
}
void Main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
for (int i = 1; i <= n; i++) { // 这里不该有1,从2开始,单独D[1].push_back(1)就AC了,哭死
if (e[i].size() == 1) {
dp[i][a[i]] = a[i];
}
D[d[i]].push_back(i);
}
int len = n;
while (1) {
set<int> st;
while (len > 0 && !D[len].size()) {
len--;
}
if (len == 0) {
break;
}
for (auto i : D[len]) {
st.insert(fa[i]);
}
for (auto i : st) {
for (auto j : e[i]) {
if (j != fa[i]) {
for (int k = a[i]; k >= 0; k--) {
for (int kk = a[j]; kk >= 0; kk--) {
if (k >= kk) {
dp[i][k] = max(dp[i][k], dp[i][k - kk] + dp[j][kk]);
}
}
}
}
}
}
len--;
}
int ans = 0;
for (int i = 0; i <= a[1]; i++) {
ans = max(ans, dp[1][i]);
}
cout << ans << "\n";
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
AC代码
#include<bits/stdc++.h>
const int N = 1e3 + 5;
int n;
int dp[N][N], a[N];
std::vector<int> e[N];
void dfs(int x, int fa) {
if (e[x].size() == 1 && x != 1) {
dp[x][0] = 1;
dp[x][a[x]] = 1;
return;
}
for (auto u : e[x]) {
if (u != fa) {
dfs(u, x);
}
}
dp[x][0] = 1;
for (auto i : e[x]) {
if (i != fa) {
for (int j = a[x]; j >= 0; j--) {
for (int k = 0; k <= a[i] && j + k <= a[x]; k++) {
dp[x][j + k] |= dp[x][j] & dp[i][k];
}
}
}
}
}
void Main() {
std::cin >> n;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i < n; i++) {
int u, v;
std::cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
for (int i = a[1]; i >= 0; i--) {
if (dp[1][i]) {
std::cout << i << "\n";
return;
}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
H. 装修报价
第一眼感觉不太好做,赛时还搓了半天组合数板子,发现没啥用。
试了好几种思路,写了个暴力DFS当对拍,来验证想法对不对,试出来了。
解题思路
- 一道不算太难的小规律题。
- \(a_i\)能否对答案产生贡献,取决于它前面的符号。
- 当\(a_i\)前面的符号是\(+\)时,可以将它变成\(-\),这样这两种方案的结果相加,就把这个数字给抵消了。
- 一个特殊的点,\(a_1\)前面的符号只能是\(+\),所以\(a_1\)(包括和它相异或的异或值)一定会产生\(a_1 \times方案数\)的贡献。
- 答案就是前\(i\)个数异或前缀和来产生贡献,下一个符号一定不是异或,后面的符号可以随便,三种其中一个,方案数\(2 \times3^k\)。
- 然后漏了一种,所有数异或在一块,额外加上就行。就是因为这个,调了半天。
- 时间复杂度\(O(nlogn)\)。
AC代码
#include<bits/stdc++.h>
using i64 = long long;
constexpr int mod = 1e9 + 7;
i64 power(i64 a, int b) {
if (b < 0) {
return 0;
}
i64 res = 1;
for (; b; a = a * a % mod, b >>= 1) {
if (b & 1) {
res = res * a % mod;
}
}
return res;
}
void Main() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int pre = a[0];
int ans = 0;
for (int i = 0; i < n - 1; i++) {
ans = (ans + pre * 2 % mod * power(3, n - i - 2) % mod) % mod;
pre ^= a[i + 1];
}
ans = (ans + pre) % mod;
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) {
Main();
}
return 0;
}
总结
总的来说,唐完了,远不如去年稳。
半小时写CDEF,四个题错两个,快速把简单题做错,然后去做麻烦的题,幸好最后一个AC了。
有的细节地方没处理好,树上背包差点AC,oi赛制自己造小数据测不出来,一判就WA。