制作 网站导航 下拉菜单外链发布软件
第十届蓝桥杯2019年C/C++ 大学A组省赛试题
做了好久, 终于把第十届的题全部做完了, 最后一题听了yls的思路后还啃了四五个小时(考试一共才四个小时啊), 还是太菜了. 没做前几届的题, 感觉这届挺难的,后面几个大题第一遍的时候都没能AC.糖果那题第一次用状压dp做, 后面三个数据超时了. 听了yls讲的改成IDA*才能AC.最后一题只是把样例过了, 试了几组大数据也没问题, 但是没找到地方测, 不能保证完全正确, 哪位小伙伴发现问题记得联系我哈.
试题 A: 平方和 (暴力)
本题总分:5 分
【问题描述】
小明对数位中含有 2、0、1、9 的数字很感兴趣,在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574,平方和是 14362。注意,平方和是指将每个数分别平方后求和。
请问,在 1 到 2019 中,所有这样的数的平方和是多少?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
提示:如果你编写程序计算,发现结果是负的,请仔细检查自己的程序, 不要怀疑考场的编程软件
#include <iostream>using namespace std;typedef long long LL;bool st[10];bool check(int n)
{while (n){if (st[n % 10]) return true;n /= 10;}return false;
}int main()
{st[0] = st[1] = st[2] = st[9] = true;LL res = 0;for (int i = 1; i <= 2019; i ++ ){if (check(i)) res += i * i;}cout << res;return 0;
}答案 2658417853
题 B: 数列求值(暴力
本题总分:5 分
【问题描述】
给定数列 1, 1, 1, 3, 5, 9, 17, …,从第 4 项开始,每项都是前 3 项的和。求
第 20190324 项的最后 4 位数字。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个 4 位整数(提示:答案的千位不为 0),在提交答案时只填写这个整数,填写多余的内容将无法得分。
#include <iostream>using namespace std;typedef long long LL;const int P = 10000;int main()
{LL a = 1, b = 1, c = 1;LL res = 0;for (int i = 4; i <= 20190324; i ++ ){res = (a + b + c) % P;a = b, b = c, c = res;}cout << res;return 0;
}答案 4659
试题 C: 最大降雨量
本题总分:10 分
【问题描述】
由于沙之国长年干旱,法师小明准备施展自己的一个神秘法术来求雨。
这个法术需要用到他手中的 49 张法术符,上面分别写着 1 至 49 这 49 个
数字。法术一共持续 7 周,每天小明都要使用一张法术符,法术符不能重复使用。
每周,小明施展法术产生的能量为这周 7 张法术符上数字的中位数。法术
施展完 7 周后,求雨将获得成功,降雨量为 7 周能量的中位数。
由于干旱太久,小明希望这次求雨的降雨量尽可能大,请大最大值是多少?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
? ? ? ? ? ? ?
? ? ? ? ? ? ?
? ? ? ? ? ? ?
? ? ? 34 35 36 37
? ? ? 38 39 40 41
? ? ? 42 43 44 45
? ? ? 46 47 48 49
答案 34
试题D: 迷宫(bfs)
【问题描述】
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可 以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。 对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。 对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式, 其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。 请注意在字典序中D<L<R<U。
【题目给出的数据】
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>using namespace std;typedef pair<int, int> PII;const int N = 55;int n, m;
char g[N][N];
pair<PII, char> pre[N][N];
bool st[N][N];
int dx[] = {1, 0, 0, -1}, dy[] = {0, -1, 1, 0};
char os[5] = "DLRU";void bfs()
{queue<PII> q;PII t;t.first = 0, t.second = 0;q.push(t);st[0][0] = true;while (!q.empty()){PII u = q.front();q.pop();int x = u.first, y = u.second;if (x == n - 1 && y == m - 1) break;for (int i = 0; i < 4; i ++ ){int a = x + dx[i], b = y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (st[a][b] || g[a][b] == '1') continue;st[a][b] = true;pre[a][b].first = u, pre[a][b].second = os[i];PII v(a, b);q.push(v); }}vector<char> path;int x = n - 1, y = m - 1;while (x != 0 || y != 0){path.push_back(pre[x][y].second);PII t = pre[x][y].first;x = t.first, y = t.second;}reverse(path.begin(), path.end());for (int i = 0; i < path.size(); i ++ )cout << path[i];
}int main()
{cin >> n >> m;for (int i = 0; i < n; i ++ ) cin >> g[i];bfs();return 0;
}答案
DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUU
RRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR
试题E: RSA 解密 (数论)
【问题描述】
RSA 是一种经典的加密算法。它的基本加密过程如下。
首先生成两个质数 p, q,令 n = p · q,设 d 与 (p − 1) · (q − 1) 互质,则可找到 e 使得 d · e 除 (p − 1) · (q − 1) 的余数为 1。n, d, e 组成了私钥,n, d 组成了公钥。当使用公钥加密一个整数 X 时(小于 n),计算 C = X^d mod n,则 C 是加密后的密文。
当收到密文 C 时,可使用私钥解开,计算公式为 X = C^e mod n。例如,当 p = 5, q = 11, d = 3 时,n = 55, e = 27。若加密数字 24,得 243 mod 55 = 19。解密数字 19,得 1927 mod 55 = 24。现在你知道公钥中 n = 1001733993063167141, d = 212353,同时你截获了别人发送的密文 C = 20190324,请问,原文是多少?
思路:
1: 将n分解质因数求出p, q, 算出d = (p - 1) * (q - 1)
2: 扩展欧几里得求出d模(p - 1)(q - 1)的逆元e
3:快速幂算出X = C^e mod n (龟速乘优化)
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;LL n = 1001733993063167141;
LL d = 212353, C = 20190324;
LL p, q, e, X;LL mul(LL a, LL b, LL P) // 龟速乘
{LL res = 0;while (b){if (b & 1) res = (res + a) % P;a = (a + a) % P;b >>= 1;}return res;
}LL qmi(LL a, LL b, LL P) // 快速幂
{LL res = 1 % P;while (b){if (b & 1) res = mul(res, a, P);a = mul(a, a, P);b >>= 1; }return res;
}LL exgcd(LL a, LL b, LL &x, LL &y) // 扩展欧几里得求逆元
{if (!b){x = 1, y = 0;return a;}LL d = exgcd(b, a % b, y, x);y -= a / b * x;return d;
}int main()
{for (LL i = 2; i <= n / i; i ++ )if (n % i == 0) p = i, q = n / i;// p = 891234941, q = 1123984201; LL u = (p - 1) * (q - 1);cout << "u = " << u << endl;LL k;exgcd(d, u, e, k);while (e < 0) e += u;cout << "e = " << e << endl;X = qmi(C, e, n);cout << "X = " << X << endl;return 0;
}
试题F: 完全二叉树的权值 (暴力)
【问题描述】
给定一棵包含 N 个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是 A1, A2, · · · AN
现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是 1。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · AN 。
【输出格式】
输出一个整数代表答案。
【样例输入】
7
1 6 5 4 3 2 1
【样例输出】
2
水题直接上代码
#include <iostream>
#include <cstdio>using namespace std;const int N = 1e5 + 10;int n, ans;
long long maxv = -1e9;
int w[N], pow[30];int main()
{pow[0] = 1;for (int i = 1; i <= 30; i ++ ) pow[i] = 2 * pow[i - 1];scanf("%d", &n);for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);int h = 1;long long sum = 0;for (int i = 1; i <= n; i ++ ){sum += w[i];if (i == pow[h] - 1){if (sum > maxv){maxv = sum;ans = h;}sum = 0;h ++ ;}}if (sum > maxv) ans = h;cout << ans;return 0;
}
试题G: 外卖店优先级 (模拟)
【问题描述】
“饱了么”外卖系统中维护着 N 家外卖店,编号 1 ∼ N。每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中。
【输入格式】
第一行包含 3 个整数 N、M 和 T 。
以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到一个订单。
【输出格式】
输出一个整数代表答案。
【样例输入】
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
【样例输出】
1
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>using namespace std;const int N = 1e5 + 10;int n, m, T;
int last[N], w[N];
bool st[N];
pair<int, int> works[N];int main()
{scanf("%d%d%d", &n, &m, &T);for (int i = 0; i < m; i ++ )scanf("%d%d", &works[i].first, &works[i].second);sort(works, works + m);for (int i = 0; i < m; i ++ ){int t = works[i].first, id = works[i].second;int sub = t - last[id] - (t == last[id] ? 0 : 1);if (w[id] - sub < 0) w[id] = 0;else w[id] -= sub;if (w[id] <= 3) st[id] = false;w[id] += 2;if (w[id] > 5) st[id] = true;last[id] = t;}int cnt = 0;for (int i = 1; i <= n; i ++ ){if (w[i] - (T - last[i]) <= 3) st[i] = false;if (st[i]) cnt ++ ;}cout << cnt;return 0;
}
试题H: 修改数组 (并查集)
【问题描述】
给定一个长度为 N 的数组 A = [A1, A2, · · · AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改
A2, A3, · · · , AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1 ∼ Ai−1 中出现过。如果出现过,则小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直到 Ai 没有在 A1 ∼ Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。现在给定初始的 A 数组,请你计算出最终的 A 数组。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · , AN 。
【输出格式】
输出 N 个整数,依次是最终的 A1, A2, · · · , AN。
【样例输入】
5
2 1 1 3 4
【样例输出】
2 1 3 4 5
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;const int N = 1e6 + 10;int n;
LL p[N];LL find(LL x)
{if (p[x] != x) p[x] = find(p[x]);return p[x];
}int main()
{for (int i = 1; i <= N; i ++ ) p[i] = i;cin >> n;while (n -- ){LL x;cin >> x;x = find(x);cout << x << ' ';p[x] = find(x + 1);}return 0;
}
试题I: 糖果 (重复覆盖问题 IDA*)
【问题描述】
糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种口味编号 1 ∼ M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而 是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
【输入格式】
第一行包含三个整数 N、M 和 K。
接下来 N 行每行 K 这整数 T1, T2, · · · , TK,代表一包糖果的口味。
【输出格式】
一个整数表示答案。如果小明无法品尝所有口味,输出 −1。
【样例输入】
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
【样例输出】
2
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;const int N = 110, M = 1 << 20;int ans;
int n, m, k;
int w[N];
vector<int> path[20];int f(int state)
{int cnt = 0;for (int i = 0; i < m; i ++ ){if (!(state >> i & 1)){for (int j = 0; j < path[i].size(); j ++ ){state |= path[i][j];}cnt ++ ;}}return cnt;
}bool dfs(int u, int state, int depth)
{if (u > depth) return false;if (u + f(state) > depth) return false;for (int i = 0; i < m; i ++ ){if (!(state >> i & 1)){for (int j = 0; j < path[i].size(); j ++ ){int v = path[i][j];if (dfs(u + 1, state | v, depth)) return true;}return false;}}return true;
}int main()
{cin >> n >> m >> k;for (int i = 1; i <= n; i ++ ){int x;for (int j = 0; j < k; j ++ ){cin >> x;w[i] |= 1 << (x - 1);}}for (int i = 0; i < m; i ++ ){for (int j = 1; j <= n; j ++ ){if (w[j] >> i & 1){path[i].push_back(w[j]);}}}int depth = 1;while (depth <= m && !dfs(0, 0, depth)) depth ++ ;if (depth > m) cout << "-1" << endl;else cout << depth << endl;return 0;
}
试题J: 组合数问题 (卢卡斯定理 + 数位dp)
【问题描述】
给 n, m, k, 求 有 多 少 对 (i, j) 满 足 1 ≤ i ≤ n, 0 ≤ j ≤ min(i, m) 且 C j ≡
0(mod k),k 是质数。其中 C j 是组合数,表示从 i 个不同的数中选出 j 个组成
一个集合的方案数。
【输入格式】
第一行两个数 t, k,其中 t 代表该测试点包含 t 组询问,k 的意思与上文中相同。
接下来 t 行每行两个整数 n, m,表示一组询问。
【输出格式】
输出 t 行,每行一个整数表示对应的答案。由于答案可能很大,请输出答案除以 109 + 7 的余数。
【样例输入】
1 2
3 3
【样例输出】
1
【样例说明】
在所有可能的情况中,只有 C(2, 1) = 2 是 2 的倍数。
【样例输入】
2 5
4 5
6 7
【样例输出】
0
7
【样例输入】
3 23
23333333 23333333
233333333 233333333
2333333333 2333333333
【样例输出】
851883128
959557926
680723120
这题数据范围很大,每组数据要用logn的做法, 具体思路参照y的讲解, 这里把代码发一下
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>using namespace std;typedef long long LL;const int N = 70, P = 1e9 + 7;LL n, m, k, num;
int t;
int f[N], g[N], pow[N];inline LL sum(int a, int b)
{return (b - a + 1) * (a + b) / 2;
}void init(vector<LL> A, vector<LL> B)
{memset(f, 0, sizeof(f));memset(g, 0, sizeof(g));for (int i = 0; i < B.size(); i ++ ){f[i] = (sum(k - B[i] + 1, k) * pow[i]) % P;if (i) f[i] = ((k - B[i]) * f[i - 1] + f[i]) % P;else f[i] = (k - B[i] + f[i]) % P;if (i) g[i] = (sum(1, A[i]) * pow[i] + (A[i] + 1) * g[i - 1]) % P;else g[i] = sum(1, A[i] + 1);}
}LL dp()
{LL ans = 0;vector<LL> nums_n, nums_m;while (n) nums_n.push_back(n % k), n /= k;while (m) nums_m.push_back(m % k), m /= k;init(nums_n, nums_m);if (nums_m.size() > nums_n.size()) return g[nums_n.size()];LL last = 0;for (int i = nums_n.size() - 1; i >= 0; i -- ){if (i >= nums_m.size()){last = last * k + nums_n[i];continue;}if (i == nums_m.size() - 1)ans = (ans + last * f[i]) % P;if (nums_m[i] > nums_n[i]){ans = (ans + sum(1, nums_n[i]) * pow[i] % P + (nums_n[i] + 1) * (i ? g[i - 1] : 1) % P) % P;break;}else{ans = (ans + sum(nums_n[i] - nums_m[i] + 1, nums_n[i]) * pow[i] % P) % P; ans = (ans + (nums_n[i] - nums_m[i]) * (i ? f[i - 1] : 1) % P) % P;ans = (ans + nums_m[i] * (i ? g[i - 1] : 1) % P) % P;}if(!i) ans ++ ; }return ans;
}LL mul(LL a, LL b, int p)
{LL res = 0;while (b){if (b & 1) res = (res + a) % p;a = (a + a) % p;b >>= 1;}return res;
}int main()
{cin >> t >> k;pow[0] = 1;int v = sum(1, k);for (int i = 1; i <= 64; i ++ ) pow[i] = ((LL)v * pow[i - 1]) % P;while (t -- ){cin >> n >> m;if (m >= n){if (n % 2 == 0) num = mul((n + 2) / 2, n + 1, P);else num = mul((n + 1) / 2, (n + 2), P);}else{if (m % 2 == 0) num = mul((n + n + 2 - m) / 2, m + 1, P);else num = mul((m + 1) / 2, n + n + 2 - m, P);}LL res = dp();cout << ((num - res) % P + P) % P << endl;}return 0;
}