2021 年蓝桥杯第一次省赛题目全解答
填空题#
卡片#
直接模拟。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
std::map<char, int> mp;
for (int i = 0; i <= 9; i++) {
mp[i + '0'] = 2021;
}
for (int i = 1; ; i++) {
for (char j : std::to_string(i)) {
if (mp[j] > 0) {
mp[j] -= 1;
} else {
std::cout << i - 1 << '\n';
std::exit(0);
}
}
}
return 0;
}
直线#
分类讨论:水平、竖直或者是斜着(记下斜率 std::atan2(y, x)
计算斜率
从而解出截距
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
std::vector<std::pair<int, int>> p;
const int n = 20, m = 21;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
p.emplace_back(i, j);
}
}
std::set<std::pair<double, double>> s;
std::set<int> kind[2]{};
int t = p.size();
for (int i = 0; i < t; i++) {
int x_0 = p[i].first, y_0 = p[i].second;
for (int j = i + 1; j < t; j++) {
int f = 0;
int x_1 = p[j].first, y_1 = p[j].second;
if (x_0 == x_1) kind[0].insert(x_0), f++;
if (y_0 == y_1) kind[1].insert(y_0), f++;
if (f == 0) {
double k = std::atan2(y_1 - y_0, x_1 - x_0);
double t = 1. * (x_0 * y_1 - x_1 * y_0) / (x_1 - x_0);
s.emplace(k, t);
}
}
}
std::cout << kind[0].size() + kind[1].size() + s.size() << '\n';
return 0;
}
货物摆放#
即求有多少可重排列,使得
路径#
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
// std::cout << 10266837 << '\n';
// return 0;
const int N = 2021 + 10;
ll g[N][N]{};
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
if (std::abs(i - j) <= 21) {
g[i][j] = g[j][i] = 1ll * i * j / std::__gcd(i, j);
} else {
g[i][j] = g[j][i] = 0x3f3f3f3f;
}
}
}
for (int k = 1; k <= 2021; k++) {
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
g[i][j] = std::min(g[i][j], g[i][k] + g[k][j]);
}
}
}
std::cout << g[1][2021] << '\n';
return 0;
}
回路计数#
即求哈密顿回路数,状压即可。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
// std::cout << "881012367360" << '\n';
// return 0;
const int n = 21;
static std::array<std::array<int, n>, n> g{};
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (std::__gcd(i + 1, j + 1) == 1) {
g[i][j] = g[j][i] = 1;
}
}
}
static std::array<std::array<ll, n>, 1 << n> f{};
f[1][0] = 1;
for (int i = 1; i < 1 << n; i++) {
for (int j = 0; j < n; j++) {
if ((i >> j & 1) && f[i][j]) { // `j` selected
for (int k = 0; k < n; k++) {
if (((i ^ (1 << j)) >> k & 1) && g[k][j]) {
f[i][j] += f[i ^ (1 << j)][k];
}
}
}
}
}
std::cout << std::accumulate(f.back().begin(), f.back().end(), 0ll) << '\n';
return 0;
}
空间#
相乘#
即求
mod = 1000000007
x = pow(2021, mod - 2, mod) * 999999999 % mod
print(x)
编程题#
砝码称重#
同 01 背包,只是转移到左右。可使用 std::bitset
的 operator<<
实现。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::bitset<100010> f;
f[0] = 1;
for (int i : a) f |= f << i;
for (int i : a) f |= f >> i;
f[0] = 0;
std::cout << f.count() << '\n';
return 0;
}
特别注意如果使用 01 背包写法,想在一个循环里完成,需要开两维。
核心代码
int B = 100000;
std::vector<std::vector<int>> f(2, std::vector<int>(2 * B + 10, 0));
f[0][0 + B] = 1;
for (int i = 1; i <= n; i++) {
int w = a[i - 1];
for (int j = -m + B; j <= m + B; j++) {
f[i & 1][j] = f[~i & 1][j];
if (j - w >= -m + B) f[i & 1][j] |= f[~i & 1][j - w];
if (j + w <= +m + B) f[i & 1][j] |= f[~i & 1][j + w];
}
}
int ans = 0;
for (int i = 1 + B; i <= m + B; i++) {
ans += f[n & 1][i];
}
异或数列#
容易发现平局当且仅当
话接上文,如果当前为出现次数
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int test = 1;
std::cin >> test;
void solve();
while (test--) solve();
return 0;
}
void solve() {
int n;
std::cin >> n;
std::array<int, 30> cnt{};
int s = 0;
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
for (int i = 20; ~i; i--) if (x >> i & 1) cnt[i] += 1;
s ^= x;
}
if (!s) {
std::cout << "0\n";
return;
}
for (int i = 20; ~i; i--) if (cnt[i] & 1) {
if (cnt[i] == 1) std::cout << "1\n";
else if ((n - cnt[i]) & 1) std::cout << "-1\n";
else std::cout << "1\n";
break;
}
}
左孩子右兄弟#
用
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<std::vector<int>> g(n + 1);
for (int i = 1; i < n; i++) {
int u;
std::cin >> u;
g[u].push_back(i + 1);
}
std::vector<int> f(n + 1);
std::function<void(int)> dfs = [&](int u) -> void {
f[u] = g[u].size();
int max = 0;
for (int v : g[u]) dfs(v), max = std::max(max, f[v]);
f[u] += max;
};
dfs(1);
std::cout << f[1] << '\n';
return 0;
}
括号序列#
既考虑左括号又考虑右括号很麻烦,不如分开考虑,最终的方案数相乘即可。如对于序列
将原始序列按照右括号分成若干片段,研究某一段还需要放多少左括号。用
展开代码
#include <bits/stdc++.h>
using ll = long long;
const int mod = 1000000007, N = 5002;
ll f[N][N];
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
std::string s;
std::cin >> s;
int n = s.size();
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
if (s[i - 1] == '(') {
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j - 1];
}
} else {
f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
for (int j = 1; j <= n; j++) {
f[i][j] = (f[i - 1][j + 1] + f[i][j - 1]) % mod;
}
}
}
ll l = -1;
for (int i = 0; i <= n; i++) {
if (f[n][i]) {
l = f[n][i];
break;
}
}
for (auto &si : s) {
si ^= '(' ^ ')';
}
std::reverse(s.begin(), s.end());
for (int i = 0; i <= n + 1; i++) {
for (int j = 0; j <= n + 1; j++) {
f[i][j] = 0;
}
}
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
if (s[i - 1] == '(') {
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j - 1];
}
} else {
f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
for (int j = 1; j <= n; j++) {
f[i][j] = (f[i - 1][j + 1] + f[i][j - 1]) % mod;
}
}
}
ll r = -1;
for (int i = 0; i <= n; i++) {
if (f[n][i]) {
r = f[n][i];
break;
}
}
std::cout << l * r % mod << '\n';
return 0;
}
时间显示#
不考虑毫秒,直接 /= 1000
,接着直接分解各位。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int test = 1;
std::cin >> test;
void solve();
while (test--) solve();
return 0;
}
void solve() {
ll x;
std::cin >> x;
x /= 1000;
ll h = 24, m = 60, s = 60;
x %= h * m * s;
std::cout << std::fixed << std::setw(2) << std::setfill('0');
std::cout << x / (m * s) << ':';
std::cout << std::fixed << std::setw(2) << std::setfill('0');
std::cout << x % (m * s) / m << ':';
std::cout << std::fixed << std::setw(2) << std::setfill('0');
std::cout << x % s << '\n';
}
iostream sucks~
杨辉三角形#
看到数据范围
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int test = 1;
std::cin >> test;
void solve();
while (test--) solve();
return 0;
}
int x;
ll binom(int n, int m) {
ll res = 1;
for (int i = n, j = 1; j <= m; i--, j++) {
res = res * i / j;
if (res > x) {
return res;
}
}
return res;
}
void solve() {
std::cin >> x;
for (int i = 16;; i--) {
int l = 2 * i, r = std::max(x, l);
while (l < r) {
int mid = (l + r) / 2;
if (binom(mid, i) >= x) r = mid;
else l = mid + 1;
}
if (binom(r, i) == x) {
std::cout << 1ll * (r + 1) * r / 2 + i + 1 << '\n';
return;
}
}
__builtin_unreachable();
}
双向排序#
注意到有许多步骤,是不需要的:例如
接下来问题转变为两种操作交替时,该如何减少操作次数。观察下面的操作:
在这里,前两次操作没有交集,这两次结果被第三次覆盖了,相当于直接从初始状态到第三步。
下面要解决的问题就只有放置了,由于任意相邻两步必有交,因此只能放那些与自己无关的区间,例如
注意如果所有操作区间的并不为
展开代码
#include <bits/stdc++.h>
using ll = long long;
const int N = 100010;
int stk[N][2], ans[N];
int top;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
while (m--) {
int p, q;
std::cin >> p >> q;
if (p == 0) {
while (top && stk[top][0] == 0)
q = std::max(q, stk[top --][1]);
while (top >= 2 && stk[top - 1][1] <= q)
top -= 2;
stk[++top][0] = p, stk[top][1] = q;
} else if (top) {
while (top && stk[top][0] == 1)
q = std::min(q, stk[top --][1]);
while (top >= 2 && stk[top - 1][1] >= q)
top -= 2;
stk[++top][0] = p, stk[top][1] = q;
}
}
int k = n, l = 1, r = n;
for (int i = 1; i <= top; i++) {
if (stk[i][0] == 0) {
while (l <= r && r > stk[i][1])
ans[r--] = k--;
} else {
while (l <= r && l < stk[i][1])
ans[l++] = k--;
}
if (l > r)
break;
}
if (top & 1) {
while (l <= r) ans[l++] = k--;
} else {
while (l <= r) ans[r--] = k--;
}
std::copy(ans + 1, ans + n + 1, std::ostream_iterator<int> {std::cout, " "});
return 0;
}
最少砝码#
实际上为平衡三进制最少需要多少底数。实际上有,
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int test = 1;
std::cin >> test;
void solve();
while (test--) solve();
return 0;
}
void solve() {
ll x;
std::cin >> x;
auto y = std::log(2 * x + 1) / std::log(3);
std::cout << (int)(std::ceil(y) + .5) << '\n';
}
作者:patricky
出处:https://www.cnblogs.com/patricky/p/2021-lanqiaocup-province-1.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人