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组题解

2021第十二届蓝桥杯国赛C/C++大学B组题解

2021第十二届蓝桥杯国赛C/C++大学B组题解

2021第十二届蓝桥杯国赛C/C++大学B组题解

第十二届蓝桥杯国赛B组 做题记录(python)

第十二届蓝桥杯国赛B组 做题记录(python)