2021蓝桥杯国赛B组C/C++个人记录
Posted As_zyh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021蓝桥杯国赛B组C/C++个人记录相关的知识,希望对你有一定的参考价值。
以下为个人场上所提交答案,欢迎大家指出错误,后续会更正正确题解。
个人提交:25
说明: Mbps是M bit per second, MB是 M Byte,bit是比特,Byte是字节,1Byte=8bit, 200/8=25
个人提交:21844 (有误)
改正:1903
说明:只有每个位都为1、3、5、7之一才是题目所说的纯质数。还有一个条件当时没注意到,这个数本身也要是素数,应该先用埃氏筛法。
代码:
#include<cstdio>
#include<cstring>
const int MAX_N = 20210605;
int num[MAX_N + 1];
int sum = 0;
void solve(int x)
int isPrime = 1;
while(x > 0)
int b = x%10; //b为x的个位数字
if(!(b == 2 || b == 3 || b == 5 || b == 7))
isPrime = 0;
break;
x = x/10;
if(isPrime) sum++;
int main()
memset(num, 1, sizeof(num));
for(int i = 2; i < 20210605; i++)
for(int j = 2; i*j <= 20210605; j++)
num[i*j] = 0;
for(int i = 1; i <= 20210605; i++)
if(num[i]) solve(i);
printf("%d\\n", sum);
return 0;
个人提交:977
说明:现场写日期类太麻烦,直接用excel搞数据,然后用c处理
先在excel中敲入这两格内容,第三格做个差,看看一共有多少天
得7699,从A1格往下拉7669行,得到从2001/1/1到2021/12/31的每一天
为了方便处理数据,把刚才不用的删掉,并且把表格另存为.csv文件
数据有了,直接全选复制,接下来用c/c++处理
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
int res = 0;
void solve(int x)
for(int i = 0; i < 100; i++)
if(i * i == x) res++;
int main()
string line;
while(getline(cin, line))
int len = line.length();
int sum = 0;
for(int i = 0; i < len; i++)
if(isdigit(line[i])) sum += line[i] - '0';
solve(sum);
cout << res << endl;
得出结果977
赛后听了老师讲,其实遍历所有日期也不难
#include<cstdio>
#include<cmath>
int days[] = 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31;
int main()
int cnt = 0;
for(int year = 2001; year <= 2021; year++)
if(year%4==0 && year%100!=0 || year%400 == 0) days[2] = 29; //闰年29天
else days[2] = 28; //平年28天
for(int month = 1; month <=12; month++)
for(int day = 1; day <= days[month]; day++)
int total = 0;
int t = year;
while(t)
total += t % 10;
t /= 10;
t = month;
while(t)
total += t % 10;
t /= 10;
t = day;
while(t)
total += t % 10;
t /= 10;
if((int)sqrt(total) * (int)sqrt(total) == total)
cnt++;
printf("%d%02d%02d %2d\\n", year, month, day, total);
printf("%d\\n", cnt);
return 0;
个人提交:2667336761
说明:赌了一把完全二叉树,可惜不对
分析:
一颗具有n个结点的最小权二叉树,假设其左子树有j个结点,其右子树的结点数为n-j-1,那么左子树一定是具有j个结点的最小权二叉树,右子树也一定是具有n-j-1个结点的最小权二叉树。–最优子结构性质
所以可以用动态规划来求解,转移方程即题目中的W(v) = 1 + 2W(L) + 3W(R ) + (C(L))2 C(R )
设 d[i]=有i个结点的二叉树的最小权值
dp[n] = min1 + 2dp[j] + 3dp[n-j-1] + j2(n-j-1), j = 0, 1, 2, …, n-1
#include<iostream>
#include<cstring>
using namespace std;
long long dp[2030];
int main()
memset(dp, 0x3f, sizeof(dp)); //每个字节均为0x3f,即无穷大
dp[0] = 0;
for(int i = 1; i < 2025; i++)
for(int j = 0; j <= i-1; j++)
if((1 + 2*dp[j] + 3*dp[i-j-1] + j*j*(i-j-1)) < dp[i])
dp[i] = 1 + 2*dp[j] + 3*dp[i-j-1] + j*j*(i-j-1);
cout << dp[2021] << endl;
return 0;
代码:
#include<cstdio>
#include<cstring>
#include<ctype.h>
char s[110];
int main()
scanf("%s", s);
for(int i = 0; i < strlen(s); i++)
printf("%c", toupper(s[i]));
return 0;
常规写法,过40%用例,评论区大佬说二分法,我在考场想到利用等差数列前n项和并根据R-L的值取分段函数,但是没来得及改,后期补上
#include<cstdio>
const int MAX_N = 200000000;
int a[MAX_N], sum = 0;
void solve()
int maxn = 1, n = 1;
for(int i = 1; i < MAX_N; i++)
a[i] = n++;
if(n > maxn)
maxn++;
n=1;
int main()
solve();
int T;
scanf("%d", &T);
for(int i = 0; i < T; i++)
int l, r;
sum = 0;
scanf("%d%d", &l, &r);
for(int i = 0; l+i <= r; i++)
sum += a[l+i];
printf("%d\\n", sum);
return 0;
当时的思路对了一部分
分析:
将数列划分为n项,其中第1项为1,第2项为1,2,第3项为1,2,3,…,第i项为1,2,…,i。显然前i项共包含了i(i+1)/2个数,最多需要不超过2*106项,即可表示数列的前1012个数。
定义f[i]表示第i项的和,sum[i]表示前i项的和.则
f[i] = i*(i+1)/2;
sum[i] = i*(i+1)*(i+2)/6;
定义函数cal(x)表示数列第1个数~第x个数的和,那么区间 [L, R] 的和就等于 cal(R ) - cal(L-1)。
而对于cal(x),我们设x在第i项第j个位置,那么可得:cal(x) = sum(i-1) + j(j+1)/2
如何求得i和j呢,随着i增大,i*(i+1)/2的值也增大,直到 i*(i+1)/2大于x,即可求出i,可以采用二分法求出i。已知i后,j可以一步算出,即 j = x - (i-1)(i)/2
时间复杂度为O(Tlogn)
#include<iostream>
using namespace std;
long long sum(long long i)
return i*(i+1)*(i+2)/6;
long long cal(long long x)
long long left = 0, right = 2e6, i;
//找到最大的i,满足 i*(i+1)/2 <= x,即找到x的前一个项
while(left <= right)
long long mid = left + right >> 1;
if(mid*(mid+1)/2 <= x)
left = mid + 1; i = mid; //因为递增求i,所以当区间左边增大时赋值
else
right = mid - 1;
long long j = x-i*(i+1)/2;
return sum(i) + j*(j+1)/2;
int main()
long long T = 1;
cin >> T;
while(T --)
long long l, r;
cin >> l >> r;
cout << cal(r) - cal(l-1) << endl;
return 0;
没找到规律,硬写,过40%用例
评论区大佬说找循环节,后期补上
#include<cstdio>
#include<cstring>
const int MAX_N = 200000000;
char s[MAX_N];
char s2[MAX_N];
int n, t;
void reverse()
s2[0] = s[0];
for(int i = 1; i < n; i++)
s2[i] = (s[i] - '0') ^ (s[i-1] - '0') + '0';
strncpy(s, s2, n);
int main()
scanf("%d%d", &n, &t);
scanf("%s", s);
for(int i = 0; i < t; i++) reverse();
printf("%s", s);
return 0;
分析:
这道题并没有考察特别难的算法,其实多模拟一些输出可以找到循环节。
将题目案例进行16次变换
10110
11101
10011 // 10
11010
10111 // 1011
11100
10010
11011
10110 // 10110 进入下一次循环
11101
10011
11010
10111
11100
10010
11011
10110
10110
经过观察我们可以发现:
前 1 位数字在异或 a 次后等于输入本身
前 2 位数字在异或 2a 次后等于输入本身
前 3 为数字在异或 4a 次后等于输入本身
前 4 位数字在异或 4a 次后等于输入本身
前 5 位数字在异或 8a 次后等于输入本身
前 6 位数字在异或 8a 次后等于输入本身
前 7 位数字在异或 8a 次后等于输入本身
前 8 位数字在异或 8a 次后等于输入本身
前 9 位数字在异或 16a 次后等于输入本身
…
可以得出,前 n 位数字在变换 2k 次后恢复初始,最小的k满足n <= 2k
所以将题目中的t去掉循环部分,求剩余部分次数的变换,可以节省大量算法复杂度,题目最大的n为10000,最大的t为1e18,最多刚好进行 10000 * 10000 次运算,可以满足时间限制1s的要求。
#include<cstdio>
#include<cmath>
long long s[10010];
long long n, t;
void printstr()
for(int i = 0; i < n; i++)
printf("%d", s[i]);
printf("\\n");
int main()
scanf("%d%d\\n", &n, &t);
for(int i = 0; i < n; i++)
s[i] = getchar() == '1' ? 1 : 0;
int len, x;
if(pow(2, (int)log(n)/log(2)) == n)
// 2的n次幂 == log(n) / log(2) ,即log2(n)为整数
x = (int)(log(n)/log(2));
len = pow(2, x);
else
x = (int)(log(n)/log(2)) + 1; //上取整
len = pow(2, x);
t = t % len;
for(int i = 0; i < t; i++)
for(int j = n; j > 0; j--)
s[j] = s[j] ^ s[j-1];
printstr();
return 0;
不会dp,硬写过30%用例
#include<cstdio>
using namespace std2021第十二届蓝桥杯国赛C/C++大学B组题解