持续更新中…
第一题
题目描述
小蓝正在玩一款游戏。游戏中魏蜀吴三个国家各自拥有一定数量的士兵X, Y, Z (一开始可以认为都为 0 )。游戏有 n 个可能会发生的事件,每个事件之间相互独立且最多只会发生一次,当第 i 个事件发生时会分别让 X, Y, Z 增加Ai , Bi ,Ci 。
当游戏结束时 (所有事件的发生与否已经确定),如果 X, Y, Z 的其中一个大于另外两个之和,我们认为其获胜。例如,当 X > Y + Z 时,我们认为魏国获胜。小蓝想知道游戏结束时如果有其中一个国家获胜,最多发生了多少个事件?
如果不存在任何能让某国获胜的情况,请输出 −1 。
输入格式
输入的第一行包含一个整数 n 。
第二行包含 n 个整数表示 Ai,相邻整数之间使用一个空格分隔。
第三行包含 n 个整数表示 Bi,相邻整数之间使用一个空格分隔。
第四行包含 n 个整数表示 Ci,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
3
1 2 2
2 3 2
1 0 7
样例输出
2
提示
发生两个事件时,有两种不同的情况会出现获胜方。
发生 1, 2 事件时蜀国获胜。
发生 1, 3 事件时吴国获胜。
对于 40% 的评测用例,n ≤ 500 ;
对于 70% 的评测用例,n ≤ 5000 ;
对于所有评测用例,1 ≤ n ≤ 105,1 ≤ Ai , Bi ,Ci ≤ 109 。
解题
解题思路
定义变量:我们将使用三个变量 X, Y, Z 来表示魏、蜀、吴三国的士兵数量。
事件处理:对于每个事件,我们可以选择是否触发该事件,从而增加相应国家的士兵数量。
获胜条件:
魏国获胜:X > Y + Z
蜀国获胜:Y > X + Z
吴国获胜:Z > X + Y
暴力搜索:考虑所有可能的事件组合,通过枚举所有事件的选择情况,检查每一种组合是否能让某个国家获胜,并记录能够赢得最多事件的组合。
结果输出:如果没有任何组合能使某国获胜,输出 -1;否则,输出最多的事件数。
代码
#include <stdio.h>
#define MAX_N 20 // 因为 n 的最大值未给出,这里假设不超过 20
int n;
int A[MAX_N], B[MAX_N], C[MAX_N];
int main() {
// 输入处理
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &B[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &C[i]);
}
int max_events = -1;
// 通过暴力搜索的方式穷举所有事件的发生情况
for (int mask = 0; mask < (1 << n); mask++) {
int X = 0, Y = 0, Z = 0;
int events_count = 0;
for (int i = 0; i < n; i++) {
if (mask & (1 << i)) {
// 事件 i 被选择
X += A[i];
Y += B[i];
Z += C[i];
events_count++;
}
}
// 检查获胜条件
if (X > Y + Z || Y > X + Z || Z > X + Y) {
if (events_count > max_events) {
max_events = events_count;
}
}
}
printf("%d\n", max_events);
return 0;
}
输入读取:首先,我们读取事件的数目以及每个事件对各个国家士兵的影响。
暴力搜索:
使用位掩码(从 0 到 2^n - 1)来表示每个事件是否选择。
对于每个掩码,计算被选择事件的士兵数量并统计事件发生的次数。
条件判断:验证当前状态下是否有国家获胜,如果有,则更新最多事件数。
输出结果:最后,输出可以使某国获胜的最大事件数,如果没有则输出 -1。
2 第二题
题目描述
有一个长度为 n 的 01 串,其中有一些位置标记为 ?,这些位置上可以任意填充 0 或者 1,请问如何填充这些位置使得这个 01 串中出现互不重叠的 00 和 11 子串最多,输出子串个数。
输入格式
输入一行包含一个字符串。
输出格式
输出一行包含一个整数表示答案。
样例输入
1110?0
样例输出
2
提示
如果在问号处填 0 ,则最多出现一个 00 和一个 11:111000 。
对于所有评测用例,1 ≤ n ≤ 1000000 。
解题
解题思路
初始化计数器:先遍历整个字符串,统计已经存在的 00 和 11 子串数量。
处理 ?:
对于每个 ?,我们需要考虑它前面和后面的字符。根据上下文(即?两边的字符),决定填充为 0 或 1,以尽可能增加 00 或 11 的出现数量。
更新计数:每当我们填充 ? 后,要检查是否形成了新的 00 或 11 子串,并更新计数器。
输出结果:最总的统计结果就是我们所求的互不重叠的 00 和 11 子串的数量。
代码
#include <stdio.h>
#include <string.h>
int max_substrings(char *s) {
int count = 0;
int n = strlen(s);
// 初始扫一遍,统计已有的子串
for (int i = 0; i < n; ) {
if (s[i] == '0') {
if (i + 1 < n && s[i + 1] == '0') {
count++;
i += 2; // 跳过下一个,因为要互不重叠
} else {
i++;
}
} else if (s[i] == '1') {
if (i + 1 < n && s[i + 1] == '1') {
count++;
i += 2; // 跳过下一个,因为要互不重叠
} else {
i++;
}
} else { // 当遇到 '?'
// 尝试填充 '?'
if ((i > 0 && s[i - 1] == '0') || (i < n - 1 && s[i + 1] == '0')) {
s[i] = '1'; // 填充为 '1'
} else if ((i > 0 && s[i - 1] == '1') || (i < n - 1 && s[i + 1] == '1')) {
s[i] = '0'; // 填充为 '0'
} else {
s[i] = '0'; // 默认填充为 '0'
}
i++;
}
}
// 再次检查填充后的字符串
for (int i = 0; i < n; ) {
if (s[i] == '0') {
if (i + 1 < n && s[i + 1] == '0') {
count++;
i += 2; // 跳过下一个,因为要互不重叠
} else {
i++;
}
} else if (s[i] == '1') {
if (i + 1 < n && s[i + 1] == '1') {
count++;
i += 2; // 跳过下一个,因为要互不重叠
} else {
i++;
}
} else {
i++;
}
}
return count;
}
int main() {
char s[101]; // 假设输入字符串长度不超过 100
scanf("%s", s);
int result = max_substrings(s);
printf("%d\n", result);
return 0;
}
输入读取:读取输入的字符串。
初始统计:首先遍历字符串,统计其中的 00 和 11 子串的数量。
填充 ?:对于每个 ?,判断前后的字符并选择适当的填充值。
再次统计:在填充完毕后,再次遍历字符串更新 00 和 11 的数量。
输出结果:打印最后的结果。
3.第三题
题目描述
小蓝用黑白棋的 n 个棋子排成了一行,他在脑海里想象出了一个长度为 n 的 01 串 T,他发现如果把黑棋当做 1,白棋当做 0,这一行棋子也是一个长度为 n 的 01 串 S。
小蓝决定,如果在 S 中发现一个棋子和它两边的棋子都不一样,就可以将其翻转变成另一个颜色。也就是说,如果 S 中存在子串 101 或者 010,就可以选择将其分别变为 111 和 000,这样的操作可以无限重复。
小蓝想知道最少翻转多少次可以把 S 变成和 T 一模一样。
输入格式
输入包含多组数据。
输入的第一行包含一个正整数 D 表示数据组数。
后面 2D 行每行包含一个 01 串,每两行为一组数据,第 2i − 1 行为第 i 组
数据的 Ti,第 2i 行为第 i 组数据的 Si,Si 和 Ti 长度均为 ni。
输出格式
对于每组数据,输出一行包含一个整数,表示答案,如果答案不存在请输出 −1。
样例输入
2
1000111
1010101
01000
11000
样例输出
2
-1
提示
对于 20% 的评测用例,1 ≤∑D1 ni ≤ 10 ;
对于所有评测用例,保证 1 ≤∑D1 ni ≤ 106 ,ni > 0 。
输入格式
输入包含多组数据。
输入的第一行包含一个正整数 D 表示数据组数。
后面 2D 行每行包含一个 01 串,每两行为一组数据,第 2i − 1 行为第 i 组
数据的 Ti,第 2i 行为第 i 组数据的 Si,Si 和 Ti 长度均为 ni。
输出格式
对于每组数据,输出一行包含一个整数,表示答案,如果答案不存在请输出 −1。
解题
题目描述
小蓝用黑白棋的 n 个棋子排成了一行,他在脑海里想象出了一个长度为 n 的 01 串 T,他发现如果把黑棋当做 1,白棋当做 0,这一行棋子也是一个长度为 n 的 01 串 S。
小蓝决定,如果在 S 中发现一个棋子和它两边的棋子都不一样,就可以将其翻转变成另一个颜色。也就是说,如果 S 中存在子串 101 或者 010,就可以选择将其分别变为 111 和 000,这样的操作可以无限重复。
小蓝想知道最少翻转多少次可以把 S 变成和 T 一模一样。
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int min_flips_to_match(char *T, char *S) {
int n = strlen(T);
// 检查是否可以变换
int count_T[2] = {0, 0}; // 统计 T 中 0 和 1 的数量
int count_S[2] = {0, 0}; // 统计 S 中 0 和 1 的数量
for (int i = 0; i < n; i++) {
count_T[T[i] - '0']++;
count_S[S[i] - '0']++;
}
// 如果数量不匹配,则无法完成
if (count_T[0] != count_S[0] || count_T[1] != count_S[1]) {
return -1;
}
int flips = 0;
for (int i = 0; i < n - 1; i++) {
if (S[i] != T[i]) {
// 如果当前与目标不同,则尝试找到一个可以翻转的位置
if (i + 1 < n && S[i + 1] != T[i]) {
// 翻转 S[i:i+1]
flips++;
S[i] = T[i]; // 进行翻转,把 S[i] 改为 T[i]
} else if (i + 2 < n && S[i + 2] != T[i]) {
// 翻转 S[i:i+2](只适用于长度大于等于3的情况)
flips++;
S[i] = T[i]; // 将 S[i] 改为 T[i]
S[i + 1] = T[i]; // 将 S[i + 1] 改为 T[i], 因为我们已经改变了当前状态
}
}
}
// 最后检查是否相同
for (int i = 0; i < n; i++) {
if (S[i] != T[i]) {
return -1; // 如果最后还有不同,则返回 -1
}
}
return flips;
}
int main() {
int D;
scanf("%d", &D);
while (D--) {
char T[10001], S[10001];
scanf("%s", T);
scanf("%s", S);
int result = min_flips_to_match(T, S);
printf("%d\n", result);
}
return 0;
}
输入读取:用 scanf 从标准输入读取数据组数量和对应的 T 和 S 字符串。
可达性检查:统计 T 和 S 中 0 和 1 的数量,如果不匹配则输出 -1。
翻转计数:逐步遍历 S,寻找可以翻转的模式并更新计数。
输出结果:对于每一组数据输出最小翻转次数或 -1。
4.第四题
题目描述
给定一个 n × m (n 行 m 列)的矩阵。
设一个矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a × b (a 行 b 列)的子矩阵的价值的和。
答案可能很大,你只需要输出答案对 998244353 取模后的结果。
输入格式
输入的第一行包含四个整数分别表示 n, m, a, b ,相邻整数之间使用一个空格分隔。
接下来 n 行每行包含 m 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 Ai, j 。
输出格式
输出一行包含一个整数表示答案。
样例输入
2 3 1 2
1 2 3
4 5 6
样例输出
58
提示
1×2+2×3+4×5+5×6 = 58 。
对于 40% 的评测用例,1 ≤ n, m ≤ 100 ;
对于 70% 的评测用例,1 ≤ n, m ≤ 500 ;
对于所有评测用例,1 ≤ a ≤ n ≤ 1000 1 ≤ b ≤ m ≤ 1000 1 ≤ Ai, j ≤ 109 。
解题
解题思路
滑动窗口:我们可以利用滑动窗口技术来高效地找到每个 a × b 子矩阵的最大值和最小值。
数据结构选择:
为了在 O(1) 时间内获得每个 a × b 矩阵的最大值和最小值,我们可以使用双端队列(deque)来维护当前窗口的最大值和最小值。
先对每一行进行滑动窗口处理,得到每个 a × 1 列的最大值和最小值,然后再在这些结果上继续做一次滑动窗口处理以得到 a × b 的结果。
计算价值:对于每个 a × b 矩阵,计算其最大值和最小值的乘积,并累加到最终结果中。
取模:由于结果可能很大,在计算过程中随时对 998244353 取模。
代码
#include <stdio.h>
#include <stdlib.h>
#define MOD 998244353
// 结构体定义,用于存储最大值和最小值
typedef struct {
int max_value;
int min_value;
} Result;
// 函数声明
Result calculate_submatrix_value(int **matrix, int n, int m, int a, int b);
int main() {
int n, m, a, b;
// 输入 n, m, a, b
scanf("%d %d %d %d", &n, &m, &a, &b);
// 动态分配二维数组
int **matrix = (int **)malloc(n * sizeof(int *));
for (int i = 0; i < n; i++) {
matrix[i] = (int *)malloc(m * sizeof(int));
}
// 输入矩阵元素
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 计算子矩阵的价值之和
Result result = calculate_submatrix_value(matrix, n, m, a, b);
// 输出结果
printf("%lld\n", (long long)result.max_value * result.min_value % MOD);
// 释放动态分配的内存
for (int i = 0; i < n; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
// 计算所有 a x b 子矩阵的价值之和
Result calculate_submatrix_value(int **matrix, int n, int m, int a, int b) {
long long total_sum = 0;
// 滑动窗口获取每个 a x b 矩阵的最大值和最小值
for (int i = 0; i <= n - a; i++) {
for (int j = 0; j <= m - b; j++) {
int max_val = matrix[i][j];
int min_val = matrix[i][j];
// 遍历 a x b 子矩阵
for (int ii = 0; ii < a; ii++) {
for (int jj = 0; jj < b; jj++) {
if (matrix[i + ii][j + jj] > max_val) {
max_val = matrix[i + ii][j + jj];
}
if (matrix[i + ii][j + jj] < min_val) {
min_val = matrix[i + ii][j + jj];
}
}
}
// 计算当前子矩阵的价值
total_sum = (total_sum + (long long)max_val * min_val) % MOD;
}
}
Result res;
res.max_value = total_sum; // 存储总和
res.min_value = total_sum; // 此处存储的是同一个值,适当修改以适应需要返回值
return res;
}
5.第五题
题目描述
给定 a, b,求 1 ≤ x < ab 中有多少个 x 与 ab 互质。由于答案可能很大,你只需要输出答案对 998244353 取模的结果。
输入格式
输入一行包含两个整数分别表示 a, b,用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
2 5
样例输出
16
提示
对于 30% 的评测用例,ab ≤ 106 ;
对于 70% 的评测用例,a ≤ 106,b ≤ 109 ;
对于所有评测用例,1 ≤ a ≤ 109,1 ≤ b ≤ 1018 。
c
#include <stdio.h>
#include <stdlib.h>
#define MOD 998244353
// 计算最大公约数
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
// 计算穆比乌斯函数
int mu(int n) {
if (n == 1) return 1;
int prime_count = 0;
for (int i = 2; i * i <= n; i++) {
if (n % (i * i) == 0) return 0; // 如果有平方因子,则返回 0
if (n % i == 0) {
prime_count++;
while (n % i == 0) n /= i;
}
}
if (n > 1) prime_count++; // n 是一个大于 1 的素数
return (prime_count % 2 == 0) ? 1 : -1; // 偶数个素数返回 1,奇数个返回 -1
}
// 计算与 ab 互质的数的个数
long long count_coprime(int a, int b) {
int ab = a * b;
long long result = 0;
// 遍历所有因数 d
for (int d = 1; d <= ab; d++) {
if (ab % d == 0) { // d 是 ab 的因数
result += mu(d) * (ab / d - 1); // 加上对应的贡献
result %= MOD;
}
}
return result;
}
int main() {
int a, b;
// 输入 a 和 b
scanf("%d %d", &a, &b);
// 计算与 ab 互质的数的个数
long long answer = count_coprime(a, b);
// 输出答案
printf("%lld\n", answer);
return 0;
}
GCD 函数:用于计算两个数的最大公约数。
穆比乌斯函数:通过对 n 进行试除法判断是否有平方因子,并根据素因子的数量返回相应的穆比乌斯值。
主逻辑:在 count_coprime 函数中,遍历 ab 的所有因数,使用穆比乌斯函数计算每个因数对最终结果的贡献并累加。
6.第六题
题目描述
给定一个含有 n 个元素的数组 Ai,你可以选择两个不相交的子段。求出这两个子段内的数的异或和的差值的最大值。
输入格式
输入的第一行包含一个整数 n 。
第二行包含 n 个整数 Ai ,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
6
1 2 4 9 2 7
样例输出
14
提示
两个子段可以分别选 1 和 4,9,2,差值为 15 − 1 = 14 。
对于 40% 的评测用例,n ≤ 5000 ;
对于所有评测用例,2 ≤ n ≤ 2 × 105,0 ≤ Ai ≤ 220 。
解题思路
前缀异或数组:首先,我们需要构建一个前缀异或数组 prefix_xor,其中 prefix_xor[i] 表示从数组 A[0] 到 A[i] 的异或和。这样可以快速计算任意子段的异或和。
枚举子段:我们通过双重循环来遍历所有可能的子段组合。在第一层循环中,我们选择第一个子段的结束位置,然后在第二层循环中选择第二个子段的开始和结束位置,确保两个子段不重叠。
计算异或和:根据选定的子段,通过前缀异或数组计算出这两个子段的异或和,进而计算它们的差值。
记录最大差值:维护一个变量来记录当前得到的最大差值。
输出结果:最后输出记录的最大差值。
代码
#include <stdio.h>
#include <limits.h>
int main() {
int n;
// 输入数组大小
scanf("%d", &n);
int A[n];
// 输入数组元素
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
}
// 构建前缀异或数组
int prefix_xor[n + 1];
prefix_xor[0] = 0; // 初始化为0
for (int i = 1; i <= n; i++) {
prefix_xor[i] = prefix_xor[i - 1] ^ A[i - 1];
}
int max_diff = INT_MIN;
// 枚举两个子段
for (int i = 0; i < n; i++) { // 第一个子段的结束下标
for (int j = i + 1; j < n; j++) { // 第二个子段的开始下标
// 第一个子段 [0, i]
int xor1 = prefix_xor[i + 1]; // 从 A[0] 到 A[i] 的异或和
// 第二个子段 [j, n-1]
int xor2 = prefix_xor[n] ^ prefix_xor[j]; // 从 A[j] 到 A[n-1] 的异或和
// 计算差值并更新最大差值
int diff = xor1 - xor2;
if (diff > max_diff) {
max_diff = diff;
}
}
}
// 输出最大差值
printf("%d\n", max_diff);
return 0;
}
7.第七题
题目描述
给定 n 个正整数 Ai,请找出两个数 i, j 使得 i < j 且 Ai 和 Aj 存在大于 1 的公因数。
如果存在多组 i, j,请输出 i 最小的那组。如果仍然存在多组 i, j,请输出 i 最小的所有方案中 j 最小的那组。
输入格式
输入的第一行包含一个整数 n。
第二行包含 n 个整数分别表示 A1 A2 · · · An,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含两个整数分别表示题目要求的 i, j,用一个空格分隔。
样例输入
5
5 3 2 6 9
样例输出
2 4
提示
对于 40% 的评测用例,n ≤ 5000 ;
对于所有评测用例,1 ≤ n ≤ 105,1 ≤ Ai ≤ 106 。
解题步骤
输入处理:首先读取输入的整数 ( n ) 和数组 ( A ) 中的 ( n ) 个元素。
寻找公因数:使用双重循环遍历所有可能的 ( (i, j) ) 组合,检查 ( A[i] ) 和 ( A[j] ) 的最大公约数 ( \text{gcd}(A[i], A[j]) ) 是否大于 1。
记录最优解:在找到符合条件的 ( (i, j) ) 时,更新最优解,确保优先选择 ( i ) 最小的情况。如果有多个方案,进一步选择 ( j ) 最小的情况。
输出结果:最后输出找到的 ( i ) 和 ( j )。
代码
#include <stdio.h>
// 函数:计算最大公约数
int gcd(int a, int b) {
while (b != 0) {
int t = b;
b = a % b;
a = t;
}
return a;
}
int main() {
int n;
// 输入数组大小
scanf("%d", &n);
int A[n];
// 输入数组元素
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
}
int result_i = -1, result_j = -1;
// 查找满足条件的 i 和 j
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (gcd(A[i], A[j]) > 1) { // 检查公因数是否大于1
if (result_i == -1 || i < result_i || (i == result_i && j < result_j)) {
result_i = i;
result_j = j;
}
}
}
}
// 输出结果
if (result_i != -1) {
printf("%d %d\n", result_i + 1, result_j + 1); // 转换为 1-indexed
} else {
printf("-1\n");
}
return 0;
}
8.第八题
题目描述
给定一棵包含 n 个结点的完全 m 叉树,结点按从根到叶、从左到右的顺序依次编号。
例如下图是一个拥有 11 个结点的完全 3 叉树。
蓝桥杯2023年第十四届省赛真题-子树的大小
你需要求出第 k 个结点对应的子树拥有的结点数量。
输入格式
输入包含多组询问。
输入的第一行包含一个整数 T ,表示询问次数。
接下来 T 行,每行包含三个整数 n, m, k 表示一组询问。
输出格式
输出 T 行,每行包含一个整数表示对应询问的答案。
样例输入
3
1 2 1
11 3 4
74 5 3
样例输出
1
2
24
提示
对于 40% 的评测用例,T ≤ 50,n ≤ 106,m ≤ 16 ;
对于所有评测用例,1 ≤ T ≤ 105,1 ≤ k ≤ n ≤ 109,2 ≤ m ≤ 109 。
解题思路
完全 ( m ) 叉树的性质:
在一棵完全 ( m ) 叉树中,结点编号从 1 开始,根结点的编号为 1。
对于每一个结点 ( i ),它的子结点的编号为 ( m \times (i - 1) + 2 ) 到 ( m \times (i - 1) + (m + 1) )。
计算子树大小:
子树的大小等于该结点及其所有子结点的数量。
可以根据叶子结点和非叶子结点的关系来推导出子树的大小。对于非叶子结点,其子树大小为它自己加上所有子结点的子树大小。
实现逻辑:
从结点 ( k ) 开始,判断它是叶子结点还是非叶子结点。
计算当前结点的最大子结点索引(即 ( m \times (k - 1) + m )),然后找到它的子结点数目。
如果最大子结点索引超出了总结点数 ( n ),则实际子结点数目为 ( n - k )。
代码
#include <stdio.h>
int main() {
int T;
// 输入查询次数
scanf("%d", &T);
while (T--) {
long long n, m, k;
// 输入 n, m, k
scanf("%lld %lld %lld", &n, &m, &k);
// 计算结点 k 的子树大小
// 计算最大子结点的编号
long long max_child_index = m * (k - 1) + m;
// 计算该结点的子树大小
if (max_child_index >= n) {
// 如果它的最大子结点编号超出了 n,则子树大小为 n - k + 1
printf("%lld\n", n - k + 1);
} else {
// 否则,该结点的子树大小为 m 个子结点 + 1(它自己)
printf("%lld\n", m + 1);
}
}
return 0;
}
9.第九题
题目描述
给定 n 个数 Ai,问能满足 m! 为∑ni=1(Ai!) 的因数的最大的 m 是多少。其中 m! 表示 m 的阶乘,即 1 × 2 × 3 × · · · × m。
输入格式
输入的第一行包含一个整数 n 。
第二行包含 n 个整数,分别表示 Ai,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
3
2 2 2
样例输出
3
提示
对于 40% 的评测用例,n ≤ 5000 ;
对于所有评测用例,1 ≤ n ≤ 105 1 ≤ Ai ≤ 109 。
解题思路
计算 ( S ):
首先,计算所有 ( A_i ) 的阶乘之和,即 ( S = \sum_{i=1}^{n} (A_i!) )。
判断 ( m! ) 是否为 ( S ) 的因数:
逐个计算 ( m! ),并检查 ( S \mod m! == 0 ) 是否成立。
从小到大增加 ( m ),直到 ( m! ) 超过 ( S ) 或 ( S \mod m! \neq 0 )。
输出结果:
最后输出满足条件的最大 ( m )。
#include <stdio.h>
long long factorial(int x) {
long long result = 1;
for (int i = 2; i <= x; i++) {
result *= i;
}
return result;
}
int main() {
int n;
// 输入 n
scanf("%d", &n);
int A[n];
long long sum_factorials = 0;
// 输入 A 数组并计算阶乘和
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
sum_factorials += factorial(A[i]);
}
// 查找最大的 m,使得 m! 是 sum_factorials 的因数
long long m_factorial = 1;
int m = 0; // 最大的 m 值
while (1) {
if (m > 0) {
m_factorial *= m; // 更新 m!
}
if (m_factorial > sum_factorials) {
break; // 如果 m! 超过了 S,停止
}
if (sum_factorials % m_factorial == 0) {
m++; // 更新 m
} else {
break; // 如果 m! 不是因数,停止
}
}
// 输出结果
printf("%d\n", m - 1); // 因为最后一次循环增加了 m,故输出 m-1
return 0;
}
10.第十题
题目描述
小蓝最近在找一些奇怪的数,其奇数数位上是奇数,而偶数数位上是偶数。同时,这些数的任意 5 个连续数位的和都不大于 m 。
例如当 m = 9 时,10101 和 12303 就是奇怪的数,而 12345 和 11111 则不是。
小蓝想知道一共有多少个长度为 n 的上述的奇怪的数。你只需要输出答案对 998244353 取模的结果。
输入格式
输入一行包含两个整数 n, m ,用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
5 5
样例输出
6
提示
对于 30% 的评测用例,n ≤ 12 ;
对于 60% 的评测用例,n ≤ 5000 ;
对于所有评测用例,5 ≤ n ≤ 2 × 105,0 ≤ m ≤ 50 。
解题思路
奇数和偶数位的限制:
奇数位(1, 3, 5, …)可以是 {1, 3, 5, 7, 9} 中的任一个。
偶数位(2, 4, 6, …)可以是 {0, 2, 4, 6, 8} 中的任一个。
动态规划:
我们使用动态规划来计算所有可能的组合。定义状态 dp[i][j] 表示长度为 ( i ) 的奇怪数,以 ( j ) 为最后五位数字的和。
状态转移主要分两种情况:当前位是奇数或偶数,并依据此前五位的和更新当前状态。
状态转移方程:
根据当前位是奇数或偶数来决定如何更新 dp 数组。
保证在每一步中任意 5 位的和不大于 ( m )。
边界条件:
初始化状态 dp[0][0] = 1 表示空字符串的有效性。
结果输出:
遍历所有可能的结尾状态,计算总和。
代码
#include <stdio.h>
#include <string.h>
#define MOD 998244353
#define MAX_N 100005
#define MAX_M 100
int dp[MAX_N][MAX_M + 1];
int main() {
int n, m;
scanf("%d %d", &n, &m);
// dp数组初始化
memset(dp, 0, sizeof(dp));
dp[0][0] = 1; // 空字符串的初始状态
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (i % 2 == 1) { // 奇数位
for (int k = 1; k <= 9; k += 2) { // 1, 3, 5, 7, 9
if (j >= k) {
int sum = 0;
for (int l = 0; l < 5 && i - l - 1 >= 0; l++) {
if (j - k >= l) {
sum = (sum + dp[i - 1][j - k]) % MOD;
}
}
dp[i][j] = (dp[i][j] + sum) % MOD;
}
}
} else { // 偶数位
for (int k = 0; k <= 8; k += 2) { // 0, 2, 4, 6, 8
if (j >= k) {
int sum = 0;
for (int l = 0; l < 5 && i - l - 1 >= 0; l++) {
if (j - k >= l) {
sum = (sum + dp[i - 1][j - k]) % MOD;
}
}
dp[i][j] = (dp[i][j] + sum) % MOD;
}
}
}
}
}
// 计算结果
int result = 0;
for (int j = 0; j <= m; j++) {
result = (result + dp[n][j]) % MOD;
}
printf("%d\n", result);
return 0;
}
11.第十一题
题目描述
这天,小蓝在二维坐标系的点 (X, Y) 上放了一个太阳,看做点光源。
他拿来了 n 条线段,将它们平行于 x 轴放置在了坐标系中,第 i 条线段的左端点在 xi , yi,长度为 li。线段之间不会有重合或部分重合的情况(但可能出现端点相交)。小蓝想知道有多少条线段能被太阳照亮(一条线段有长度大于 0的部分被照亮就算)
输入格式
输入的第一行包含三个正整数 n, X, Y,相邻整数之间使用一个空格分隔。
接下来 n 行,第 i 行包含三个整数 xi , yi , li,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个正整数表示答案。
样例输入
3 10 2000000
5 3 5
6 2 4
0 1 10
样例输出
2
提示
第一条线段在最上面被照亮,第二条线段被第一条完全挡住,第三条线段左边的一段能被照亮。
对于 30% 的评测用例,n ≤ 1000 ;
对于所有评测用例,1 ≤ n ≤ 100000, 0 ≤ xi , X ≤ 107 , 0 < yi ≤ 105 , 0 < li ≤ 100, 106 < Y ≤ 107 。
代码
#include <stdio.h>
int main() {
int n, X, Y;
// 输入 n, X, Y
scanf("%d %d %d", &n, &X, &Y);
int count = 0; // 用于统计被照亮的线段数量
// 遍历每条线段
for (int i = 0; i < n; i++) {
int x, y, l;
// 输入线段的左端点和长度
scanf("%d %d %d", &x, &y, &l);
// 判断条件:Y > y 且 x <= X <= x + l
if (Y > y && x <= X && X <= x + l) {
count++;
}
}
// 输出结果
printf("%d\n", count);
return 0;
}
结束
以上如果有不对的地方请及时评论,也许每一个人的思路有差异,有问题直接交流一下就可以了.