2019年第十届蓝桥杯国赛C++B组

平方序列

【问题描述】
小明想找到两个正整数 X 和 Y,满足
• 2019 < X < Y;
• 20192, X2, Y2 组成等差数列。
请你求出在所有可能的解中, X + Y 的最小值是多少?

3111 + 3909 = 7020

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
	int a = 2019 * 2019;
	for(int i = 2020; i < 100000; i++){
		for(int j = i + 1; j < 100000; j++){
			if(a - (i * i) == (i * i) - (j * j)){
				cout << 2019 <<  " " << i << " " << j << endl;
			}
		}
	} 
	return 0;
}

质数拆分

【问题描述】
将 2019 拆分为若干个两两不同的质数之和,一共有多少种不同的方法?
注意交换顺序视为同一种方法,例如 2 + 2017 = 2019 与 2017 + 2 = 2019
视为同一种方法。

1……好吧看了其他人的题解,是指两两各不相同,不是分解成只有两个的质数,要用dp
正解:55965365465060
参考文章

#include<iostream>
using namespace std;
#define N 2020
int prime[N];
int fg[N];
int cnt = 0;
long long dp[N][N];
void init(){
	for(int i = 2; i <= 2019; i++){
		if(!fg[i]){
			prime[++cnt] = i;
			for(int j = 2; j * i <= 2019; j++){//筛素数 
				fg[j * i] = 1;
			}
		}
	}
}
int main(){
	init();
	//dp[i][j]表示前i个质数,可以两两不同加起来等于j的方案数
	dp[0][0] = 1;
	for(int i = 1; i <= cnt; i++){
		for(int j = 0; j <= 2019; j++){
			dp[i][j] = dp[i - 1][j];
			if(j >= prime[i]){
				dp[i][j] = dp[i - 1][j] + dp[i - 1][j - prime[i]];
			}
		}
	}
	cout << dp[cnt][2019];
	return 0; 
} 

拼接

【问题描述】
小明要把一根木头切成两段,然后拼接成一个直角。
如下图所示,他把中间部分分成了 n × n 的小正方形,他标记了每个小正方
形属于左边还是右边。然后沿两边的分界线将木头切断,将右边旋转向上后拼
接在一起。
要求每个小正方形都正好属于左边或右边,而且同一边的必须是连通的。
在拼接时,拼接的部位必须保持在原来大正方形里面。
请问,对于 7 × 7 的小正方形,有多少种合法的划分小正方形的方式。

拼接
正解:2444
参考文章

引用思路:

  1. 由于要将矩阵分割成两部分,且旋转后依旧要完美匹配;
  2. 因此被分割的两块都必须是沿着主对角线对称的;
  3. 所以要以对角线上的每一个点为起点,在搜索的过程中同时标记搜索点和对称点,当触及边界时,就完成了一次分割;
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10;
int ans;
bool used[N][N];
int dx[] = {-1,0,1,0};
int dy[] = {0,1,0,-1};
void dfs(int x, int y){
	if(x == 0 || y == 7){
		ans ++;
		return;
	}
	for(int i = 0; i < 4; i++){
		int a = x + dx[i];
		int b = y + dy[i];
		if(a < 0 || a > 7 || b < 0 || b > 7) continue;
		if(used[a][b] || a == b) continue;
		used[a][b] = used[b][a] = true;
		dfs(a,b);
		used[a][b] = used[b][a] = false; 
	}
}
int main(){
	for(int i = 0; i <= 7; i++){
		memset(used, false, sizeof used);
		used[i][i] = true;
		dfs(i,i);
	}
	cout << ans;
	return 0;
}

求值

【问题描述】
学习了约数后,小明对于约数很好奇,他发现,给定一个正整数 t,总是可
以找到含有 t 个约数的整数。小明对于含有 t 个约数的最小数非常感兴趣,并
把它定义为 S t 。
例如 S 1 = 1, S 2 = 2, S 3 = 4, S 4 = 6, · · · 。
现在小明想知道,当 t = 100 时, S t 是多少?即 S 100 是多少?

暴力求解
正解:45360
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int m = 100;
bool check(int k){
	int ans = 0;
	for(int i = 1; i <= 100000; i++){
		if(k % i == 0)
			ans ++;
	}
	return ans == m;
}
int main(){
	for(int i = 1; i <= 100000; i++){
		if(check(i)){
			cout << i ;
			return 0;
		}
	} 
	return 0;
}

路径计数

【问题描述】
从一个 5x5 的方格矩阵的左上角出发,沿着方格的边走,满足以下条件的
路线有多少种?
• 总长度不超过 12;
• 最后回到左上角;
• 路线不自交;
• 不走出 5x5 的方格矩阵范围之外。
如下图所示, ABC 是三种合法的路线。注意 B 和 C 由于方向不同,所以
视为不同的路线。

在这里插入图片描述
正解:206
参考文章

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10;
int p[N][N] = {0};
int ans = 0;
int a[] = {-1, 0, 1, 0};
int b[] = {0, 1, 0, -1};
void dfs(int x, int y, int l){
	if(l > 12) return;
	if(x == 0 && y == 0 && l > 2 && l <= 12){
		ans++;
		return;
	}
	
	for(int i = 0; i < 4; i++){
		int dx = x + a[i];
		int dy = y + b[i];
		if(p[dx][dy] == 1 || dx < 0 || dx > 7 || dy < 0 || dy > 7)
			continue;
		p[dx][dy] = 1;
		dfs(dx, dy, l+1);
		p[dx][dy] = 0;
	}

}
int main(){
	dfs(0,0,0);
	cout << ans;
	return 0;
}

最优包含

【问题描述】
我们称一个字符串 S 包含字符串 T 是指 T 是 S 的一个子序列,即可以从
字符串 S 中抽出若干个字符,它们按原来的顺序组合成一个新的字符串与 T 完
全一样。
给定两个字符串 S 和 T,请问最少修改 S 中的多少个字符,能使 S 包含T?
【输入格式】
输入两行,每行一个字符串。第一行的字符串为 S,第二行的字符串为T。
两个字符串均非空而且只包含大写英文字母。
【输出格式】
输出一个整数,表示答案。
【样例输入】
ABCDEABCD
XAABZ
【样例输出】
3
【评测用例规模与约定】
对于 20% 的评测用例, 1 ≤ |T| ≤ |S| ≤ 20;
对于 40% 的评测用例, 1 ≤ |T| ≤ |S| ≤ 100;
对于所有评测用例, 1 ≤ |T| ≤ |S| ≤ 1000。

参考文章

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1010;
int dp[N][N];
//dp[i][j]:表示T以第j个字符结尾,S的前i个字符要包含T的最少修改次数。
char str1[N], str2[N];
string s1, s2;
int main(){
	cin >> s1 >> s2;
	int size1 = s1.size();
	int size2 = s2.size();
	for(int i = 0; i < size1; i++)
		str1[i + 1] = s1[i];
	for(int i = 0; i < size2; i++)
		str2[i + 1] = s2[i];
	size1++;
	size2++;
	memset(dp,100, sizeof dp);
	dp[0][0] = 0;
	for(int i = 1; i <= size1; i++){
		dp[i][0] = 0;
		for(int j = 1; j <= size2; j++){
			if(str1[i] == str2[j])
				dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1]);
			else
				dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + 1);
		}
	}
	cout << dp[size1][size2];
	return 0;
}

排列数

【问题描述】
在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,
或者同时大于两边的元素。
对于一个 1 ∼ n 的排列,如果可以将这个排列中包含 t 个折点,则它称为
一个 t + 1 单调序列。
例如,排列 (1; 4; 2; 3) 是一个 3 单调序列,其中 4 和 2 都是折点。
给定 n 和 k,请问 1 ∼ n 的所有排列中有多少个 k 单调队列?
【输入格式】
输入一行包含两个整数 n, k。
【输出格式】
输出一个整数,表示答案。答案可能很大,你可需要输出满足条件的排列
数量除以 123456 的余数即可。
【样例输入】
4 2
【样例输出】
12
【评测用例规模与约定】
对于 20% 的评测用例, 1 ≤ k ≤ n ≤ 10;
对于 40% 的评测用例, 1 ≤ k ≤ n ≤ 20;
对于 60% 的评测用例, 1 ≤ k ≤ n ≤ 100;
对于所有评测用例, 1 ≤ k ≤ n ≤ 500。

没看懂这个DP
大佬题解链接

解谜游戏

【问题描述】
小明正在玩一款解谜游戏。谜题由 24 根塑料棒组成,其中黄色塑料棒 4
根,红色 8 根,绿色 12 根 (后面用 Y 表示黄色、 R 表示红色、 G 表示绿色)。
初始时这些塑料棒排成三圈,如上图所示,外圈 12 根,中圈 8 根,内圈 4 根。
小明可以进行三种操作:
1.将三圈塑料棒都顺时针旋转一个单位。例如当前外圈从 0 点位置开始
顺时针依次是 YRYGRYGRGGGG,中圈是 RGRGGRRY,内圈是 GGGR。那
么顺时针旋转一次之后,外圈、中圈、内圈依次变为: GYRYGRYGRGGG、
YRGRGGRR 和 RGGG。
2.将三圈塑料棒都逆时针旋转一个单位。例如当前外圈从 0 点位置开始
顺时针依次是 YRYGRYGRGGGG,中圈是 RGRGGRRY,内圈是 GGGR。那
么逆时针旋转一次之后,外圈、中圈、内圈依次变为: RYGRYGRGGGGY、
GRGGRRYR 和 GGRG
3 将三圈 0 点位置的塑料棒做一个轮换。具体来说:外圈 0 点塑料棒
移动到内圈 0 点,内圈 0 点移动到中圈 0 点,中圈 0 点移动到外圈 0 点。
例如当前外圈从 0 点位置开始顺时针依次是 YRYGRYGRGGGG,中圈是
RGRGGRRY,内圈是 GGGR。那么轮换一次之后,外圈、中圈、内圈依次变
为: RRYGRYGRGGGG、 GGRGGRRY 和 YGGR。
小明的目标是把所有绿色移动到外圈、所有红色移动中圈、所有黄色移动
到内圈。给定初始状态,请你判断小明是否可以达成目标?
【输入格式】
第一行包含一个整数 T,代表询问的组数。 (1 ≤ T ≤ 100)。
每组询问包含 3 行:
第一行包含 12 个大写字母,代表外圈从 0 点位置开始顺时针每个塑料棒
的颜色。
第二行包含 8 个大写字母,代表中圈从 0 点位置开始顺时针每个塑料棒的
颜色。
第三行包含 4 个大写字母,代表内圈从 0 点位置开始顺时针每个塑料棒的
颜色。
【输出格式】
对于每组询问,输出一行 YES 或者 NO,代表小明是否可以达成目标。
【样例输入】
2
GYGGGGGGGGGG
RGRRRRRR
YRYY
YGGGRRRRGGGY
YGGGRRRR
YGGG
【样例输出】
YES
NO

在这里插入图片描述
思路题解
参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
	int T;
	cin >> T;
	while(T--){
		string a,b,c;
		cin >> a >> b >> c;
		int flag = 0;
		for(int i = 0; i < 4; i++){
			map<char,int > mp;
			mp[a[i]]++;
			mp[a[i + 4]] ++;
			mp[a[i + 8]] ++;
			
			mp[b[i]] ++;
			mp[b[i + 4]] ++;
			
			mp[c[i]] ++;
			
			if(mp['G'] != 3 || mp['R'] != 2 || mp['Y'] != 1)
				flag = 1;
		}
		if(flag)
			cout << "NO" << '\n';
		else
			cout << "YES" << '\n';
	} 
	return 0;
}

第八大奇迹

【问题描述】
在一条 R 河流域,繁衍着一个古老的名族 Z。他们世代沿河而居,也在河
边发展出了璀璨的文明。
Z 族在 R 河沿岸修建了很多建筑,最近,他们热衷攀比起来。他们总是在
比谁的建筑建得最奇特。
幸好 Z 族人对奇特的理解都差不多,他们很快给每栋建筑都打了分,这样
评选谁最奇特就轻而易举了。
于是,根据分值,大家很快评出了最奇特的建筑,称为大奇迹。
后来他们又陆续评选了第二奇特、第二奇特、……、第七奇特的建筑,依次
称为第二大奇迹、第三大奇迹、……、第七大奇迹。
最近,他们开始评选第八奇特的建筑,准备命名为第八大奇迹。
在评选中,他们遇到了一些问题。
首先, Z 族一直在发展,有的建筑被拆除又建了新的建筑,新建筑的奇特
值和原建筑不一样,这使得评选不那么容易了。
其次, Z 族的每个人所生活的范围可能不一样,他们见过的建筑并不是所
有的建筑,他们坚持他们自己所看到的第八奇特的建筑就是第八大奇迹。
Z 族首领最近很头疼这个问题,他害怕因为意见不一致导致 Z 族发生分歧。
他找到你,他想先了解一下,民众自己认为的奇迹是怎样的。
现在告诉在 R 河周边的建筑的变化情况,以及在变化过程中一些人的生活
范围,请编程求出每个人认为的第八大奇迹的奇特值是多少。
【输入格式】
输入的第一行包含两个整数 L, N,分别表示河流的长度和要你处理的信息
的数量。开始时河流沿岸没有建筑,或者说所有的奇特值为 0。
接下来 N 行,每行一条你要处理的信息。
如果信息为 C p x,表示流域中第 p 个位置 (1 ≤ p ≤ L) 建立了一个建筑,
其奇特值为 x。如果这个位置原来有建筑,原来的建筑会被拆除。
如果信息为 Q a b,表示有个人生活的范围是河流的第 a 到 b 个位置(包
含 a 和 b, a ≤ b),这时你要算出这个区间的第八大奇迹的奇特值,并输出。如
果找不到第八大奇迹,输出 0。
【输出格式】
对于每个为 Q 的信息,你需要输出一个整数,表示区间中第八大奇迹的奇
特值。
【样例输入】
10 15
C 1 10
C 2 20
C 3 30
C 4 40
C 5 50
C 6 60
C 7 70
C 8 80
C 9 90
C 10 100
Q 1 2
Q 1 10
Q 1 8
C 10 1
Q 1 10
【样例输出】
0
30
10
20
【评测用例规模与约定】
对于 20% 的评测用例, 1 ≤ L ≤ 1000, 1 ≤ N ≤ 1000。
对于 40% 的评测用例, 1 ≤ L ≤ 10000, 1 ≤ N ≤ 10000。
对于 100% 的评测用例, 1 ≤ L ≤ 100000, 1 ≤ N ≤ 100000。所有奇特值为
不超过 109 的非负整数。

线段树
大佬做法
自己只会暴力,超时得了40/100

燃烧权杖

【问题描述】
小 C 最近迷上了一款游戏。现在,在游戏中,小 C 有一个英雄,生命值为
x;敌人也有一个英雄,生命值为 y。除此以外,还有 k 个士兵,生命值分别为
a1、 a2、……、 ak。
现在小 C 打算使用一个叫做“燃烧权杖”的技能。“燃烧权杖”会每次等概
率随机选择一个活着的角色(英雄或士兵),扣减其 10 点生命值,然后如果该
角色的生命值小于或等于 0,则该角色死亡,不会再被“燃烧权杖”选中。“燃
烧权杖”会重复做上述操作,直至任意一名英雄死亡。
小 C 想知道使用“燃烧权杖”后敌方英雄死亡(即,小 C 的英雄存活)的
概率。为了避免精度误差,你只需要输出答案模一个质数 p 的结果,具体见输
出格式。
【输入格式】
输入包含多组数据。
输入第一行包含一个正整数 T,表示数据组数。
接下来 T 组,每组数据第一行包含四个非负整数 x、 y、 p、 k,分别表示小
C 的英雄的生命值、敌方英雄的生命值,模数和士兵个数。
第二行包含 k 个正整数 a1、 a2、……、 ak,分别表示每个士兵的生命值。
【输出格式】
对于每组数据,输出一行一个非负整数,表示答案模质数 p 的余数。
可以证明,答案一定为有理数。设答案为 a/b(a 和 b 为互质的正整数),
你输出的数为 x,则你需要保证 a 与 bx 模 p 同余;也即, x = (a · b−1) mod p,
其中 b−1 表示 b 模 p 的逆元, mod 为取模运算。
【样例输入】
6
1 10 101 0
100 1 101 0
50 30 4903 2
1 1
987 654 233 1
321
1000000000 999999999 233 3
1 2 3
1000000000 999999999 3 3
1 2 3
【样例输出】
51
37
1035
118
117
2
【样例说明】
对于第一组数据,所求概率即为“燃烧权杖”第一次就扣减敌方英雄 10 点
生命值的概率,即 1/2。 2 × 51 模 101 余 1。
对于第二组数据,答案为 1023/1024, 1024 × 37 与 1023 模 101 同余。
对于第三组数据,答案为 99/128。
【评测用例规模与约定】
对于 10% 的评测用例, x; y; a1; · · · ; ak ≤ 10。
对于 20% 的评测用例, x; y; a1; · · · ; ak ≤ 100。
对于 50% 的评测用例, x; y; a1; · · · ; ak ≤ 1000。
另有 10% 的评测用例, p = 3。
另有 20% 的评测用例, p ≤ 100。
对于全部评测用例, 1 ≤ x; y; a1; · · · ; ak ≤ 109, 3 ≤ p ≤ 10000 且 p 为质数,
0 ≤ k ≤ 10。

躺平不会

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值