第十三届蓝桥杯 C++ B组题解:
A:进位制
(2022)9= 2 * 9^3 + 2 * 9+ 2=1478
B:顺子日期:(0,1,2算顺子,逆序不算)
枚举,14个
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int months[]={
0,31,28,31,30,31,
30,31,31,30,31,30,
31
};
bool check(string str)
{
for (int i = 0; i < str.size(); i ++ )
{
if(str[i+1]==str[i]+1 && str[i+2]==str[i]+2)
return true;
}
return false;
}
int main()
{
int year=2022,month=1,day=1;
int res=0;
for (int i = 0; i < 365; i ++ )
{
char str[10];
sprintf(str,"%04d%02d%02d",year,month,day);//格式化数据写入字符串
if(check(str))
{
res++;
cout << str <<endl;
}
if(++day>months[month])
{
day=1;
month++;
}
}
cout << res;
return 0;
}
C:刷题统计
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。
他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目。
请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n 题?
输入格式
输入一行包含三个整数 a,b 和 n。
输出格式
输出一个整数代表天数。
数据范围
对于 50% 的评测用例,1≤a,b,n≤106,
对于 100% 的评测用例,1≤a,b,n≤1018。
输入样例:
10 20 99
输出样例:
8
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
LL a, b, n, res;
cin >> a >> b >> n;
res = n / (a * 5 + b * 2) * 7;
n -= (a * 5 + b * 2) * res / 7;
for (int i = 0; i < 5 and n > 0; i ++) res ++, n -= a;
for (int i = 0; i < 2 and n > 0; i ++) res ++, n -= b;
cout << res << endl;
return 0;
}
D:修剪灌木
爱丽丝要完成一项修剪灌木的工作。
有 N 棵灌木整齐的从左到右排成一排。
爱丽丝在每天傍晚会修剪一棵灌木,让灌木的高度变为 0 厘米。
爱丽丝修剪灌木的顺序是从最左侧的灌木开始,每天向右修剪一棵灌木。
当修剪了最右侧的灌木后,她会调转方向,下一天开始向左修剪灌木。
直到修剪了最左的灌木后再次调转方向。
然后如此循环往复。
灌木每天从早上到傍晚会长高 1 厘米,而其余时间不会长高。
在第一天的早晨,所有灌木的高度都是 0 厘米。爱丽丝想知道每棵灌木最高长到多高。
输入格式
一个正整数 N,含义如题面所述。
输出格式
输出 N 行,每行一个整数,第行表示从左到右第 i 棵树最高能长到多高。
数据范围
对于 30% 的数据,N≤10,
对于 100% 的数据,1<N≤10000。
输入样例:
3
输出样例:
4
2
4
//模拟,找规律,找左右两边最长距离乘二
就两种情况
2(n-i) 2(i-1)的max
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cout<<2*max(n-i,i-1)<<endl;
}
E:X进制减法
进制规定了数字在数位上逢几进一。
XX 进制是一种很神奇的进制,因为其每一数位的进制并不固定!
例如说某种 X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制,则 X 进制数 321 转换为十进制数为 65。
现在有两个 X 进制表示的整数 A 和 B,但是其具体每一数位的进制还不确定,只知道 A 和 B 是同一进制规则,且每一数位最高为 N 进制,最低为二进制。
请你算出 A−B 的结果最小可能是多少。
请注意,你需要保证 A 和 B 在 X 进制下都是合法的,即每一数位上的数字要小于其进制。
输入格式
第一行一个正整数 N,含义如题面所述。
第二行一个正整数 Ma,表示 XX 进制数 A 的位数。
第三行 Ma 个用空格分开的整数,表示 XX 进制数 A 按从高位到低位顺序各个数位上的数字在十进制下的表示。
第四行一个正整数 Mb,表示 X 进制数 B 的位数。
第五行 Mb 个用空格分开的整数,表示 X 进制数 B 按从高位到低位顺序各个数位上的数字在十进制下的表示。
请注意,输入中的所有数字都是十进制的。
输出格式
输出一行一个整数,表示 X 进制数 A−B 的结果的最小可能值转换为十进制后再模 1000000007 的结果。
数据范围
对于 30% 的数据,N≤10;Ma,Mb≤8,
对于 100% 的数据,2≤N≤1000;1≤Ma,Mb≤100000;A≥B。
输入样例:
11
3
10 4 0
3
1 2 0
输出样例:
94
样例解释
当进制为:最低位 2 进制,第二数位 5 进制,第三数位 11 进制时,减法得到的差最小。
此时 A 在十进制下是 108,B 在十进制下是 14,差值是 94。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010,MOD=1000000007;
int n,m1,m2,m;
int a[N],b[N];
int main()
{
scanf("%d", &n);
scanf("%d", &m1);
for (int i = m1-1; i >= 0; i -- ) scanf("%d", &a[i]);
scanf("%d", &m2);
for (int i = m2-1; i >= 0; i -- ) scanf("%d", &b[i]);
int m=max(m1,m2);
int res=0;
for (int i = m-1; i >= 0; i -- )
res=( res * (LL)max( {2, a[i]+1, b[i]+1} ) + a[i]-b[i] ) % MOD;
printf("%d\n",res);
return 0;
}
F子矩阵的和:
输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。
接下来 n 行,每行包含 mm 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2表示一组询问。
输出格式
共 q 行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1010;
LL s[N][N];
int main()
{
int n,m,k;
cin >> n>>m>>k;
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
cin >> s[i][j];
s[i][j]+=s[i-1][j];
}
}
LL res=0;
//前缀和的题,用双指针优化
for (int i = 1; i <= n; i ++ )
for (int j = i; j <= n; j ++ )//上下边界的长度
for (int l = 1,r=1,sum=0; r <= m; r ++ )//左右边界的长度
{
sum+=s[j][r]-s[i-1][r];
while (sum>k)//l往右移
{
sum-=s[j][l]-s[i-1][l];
l++;
}
res+=r-l+1;//每次加上比这小的矩阵数
}
cout<<res;
return 0;
}
G 积木画:
小明最近迷上了积木画,有这么两种类型的积木,分别为 II 型(大小为 2 个单位面积)和 LL 型(大小为 3 个单位面积):
同时,小明有一块面积大小为 2×N 的画布,画布由 2×N个 1×1区域构成。
小明需要用以上两种积木将画布拼满,他想知道总共有多少种不同的方式?
积木可以任意旋转,且画布的方向固定。
输入格式
输入一个整数 N,表示画布大小。
输出格式
输出一个整数表示答案。
由于答案可能很大,所以输出其对 1000000007取模后的值。
数据范围
1≤N≤107。
输入样例:
3
输出样例:
5
样例解释
五种情况如下图所示,颜色只是为了标识不同的积木:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e7+10,MOD=1e9+7;
int n;
//状态压缩数组
//数组第一行00,表示i-1列没有伸到i列,
//对i列进行操作,第i+1列可能的状态为:00,01,10,11,
//转换为十进制为0,1,2,3。对应数组位置上标为1,表示可以转换到。
//数组第二行01,表示i-1列有一个伸到i列,
//对i列进行操作,第i+1列可能的状态为:10,11,
//转换为十进制为2,3。对应数组位置上标为1,表示可以转换到。
//数组第三行10,表示i-1列有一个伸到i列,
//对i列进行操作,第i+1列可能的状态为:01,11,
//转换为十进制为1,3。对应数组位置上标为1,表示可以转换到。
//数组第四行11,表示i-1列有二个伸到i列,
//对i列进行操作,(已经无法操作了,第i列都满了)第i+1列只可能的状态为:00,
//转换为十进制为1,3。对应数组位置上标为1,表示可以转换到。
int g[4][4]={
{1,1,1,1},
{0,0,1,1},
{0,1,0,1},
{1,0,0,0},
};
int f[N][4];
int main()
{
scanf("%d", &n);
f[1][0]=1;//初始情况,第1列摆好,且从1列到第2列伸出来的状态为0的方案数,只有竖着摆这1种状态
for (int i = 1; i <= n; i ++ )//枚举每一列
for (int j = 0; j < 4; j ++ )//枚举第i列的所有状态
for (int k = 0; k < 4; k ++ )//枚举第i+1列的所有状态
f[i+1][k]=(f[i+1][k]+ f[i][j]*g[j][k])%MOD;//判断第i列能否转移到第i+1列
printf("%d\n",f[n+1][0]);//前n列都处理完,并且第n列没有伸出来的所有方案数。
return 0;
}
H 扫雷:
小明最近迷上了一款名为《扫雷》的游戏。
其中有一个关卡的任务如下:
在一个二维平面上放置着 n 个炸雷,第 i 个炸雷 (xi,yi,ri)表示在坐标 (xi,yi) 处存在一个炸雷,它的爆炸范围是以半径为 ri的一个圆。
为了顺利通过这片土地,需要玩家进行排雷。
玩家可以发射 m 个排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 j 个排雷火箭(xj,yj,rj) 表示这个排雷火箭将会在 (xj,yj) 处爆炸,它的爆炸范围是以半径为 rj 的一个圆,在其爆炸范围内的炸雷会被引爆。
同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。
现在小明想知道他这次共引爆了几颗炸雷?
你可以把炸雷和排雷火箭都视为平面上的一个点。
一个点处可以存在多个炸雷和排雷火箭。
当炸雷位于爆炸范围的边界上时也会被引爆。
输入格式
输入的第一行包含两个整数 n、m。
接下来的 n 行,每行三个整数 xi,yi,ri,表示一个炸雷的信息。
再接下来的 m 行,每行三个整数 xj,yj,rj,表示一个排雷火箭的信息。
输出格式
输出一个整数表示答案。
数据范围
对于 40% 的评测用例:0≤x,y≤10^9,0≤n,m≤103,1≤r≤10,
对于 100% 的评测用例:0≤x,y≤10^9,0≤n,m≤5×104,1≤r≤10。
输入样例:
2 1
2 2 4
4 4 2
0 0 5
输出样例:
2
样例解释
示例图如下,排雷火箭 1 覆盖了炸雷 1,所以炸雷 1 被排除;炸雷 1 又覆盖了炸雷 2,所以炸雷 2 也被排除。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 5e4+10,M=1e6+7,X=1e9+1;
typedef long long ll;
int n,m;
ll h[M];// 哈希数组
bool st[N];// 判断是否访问过
int id[M], res; // id数组为哈希数组中key对应的地雷下标
struct node{
int x,y,r;
}b[N];
ll get_hs(int x, int y) { // 得到每个坐标的哈希值
return (ll)x * X + y;
}
int find(int x,int y)
{
ll hs=get_hs(x,y);
int key=(hs %M+M)%M;
while (h[key] !=-1 && h[key]!=hs)
{
key++;
if(key==M) key=0;
}
return key;
}
// 判断x1,y1为圆心,半径为r的圆是否包含x,y
bool check(int x1, int y1, int r, int x, int y) {
int d = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
return d <= r * r;
}
void bfs(int pos)
{
queue<int> q;
q.push(pos);
st[pos] = 1;
while(q.size())
{
int t = q.front();
q.pop();
int x = b[t].x, y = b[t].y, r = b[t].r;
for(int xx = x - r; xx <= x + r; xx++)
{ // 从(x-r, y-r)枚举到(x+r, y+r)
for(int yy = y - r; yy <= y + r; yy++)
{
int key = find(xx, yy); // 找到该坐标点对应的哈希下标
// 该点是不是地雷,有没有被访问过,能不能炸到
if(id[key] && !st[id[key]] && check(x, y, r, xx, yy))
{
int pos = id[key]; // 获得对应地雷编号
st[pos] = 1;
q.push(pos);
}
}
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
int x, y, r;
for(int i = 1; i <= n; i++) // 读入地雷,存入哈希表
{
cin >> x >> y >> r;
b[i] = {x, y, r};
int key=find(x,y);// 找到该坐标点对应的哈希下标
if(h[key] == -1) h[key] = get_hs(x, y);
if(!id[key] || b[id[key]].r < r)
{
id[key] = i;
}
}
for(int i = 1; i <= m; i++) { // 枚举导弹
cin >> x >> y >> r;
for(int xx = x - r; xx <= x + r; xx++) {
for(int yy = y - r; yy <= y + r; yy++) {
int key = find(xx, yy);
// 如果该点有地雷,没有被访问过,能被炸到
if(id[key] && !st[id[key]] && check(x, y, r, xx, yy))
bfs(id[key]);
}
}
}
// 遍历每个地雷,看是否被标记
for(int i = 1; i <= n; i++)
{
int key = find(b[i].x, b[i].y); // 获得坐标点对应哈希下标
int pos = id[key]; // 哈希下标对应的地雷编号
if(pos && st[pos]) res++; // 如果有地雷并且被访问过
}
cout << res;
return 0;
}
I李白打酒加强版:
话说大诗人李白,一生好饮。
幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2 斗。
他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店 N 次,遇到花 M 次。
已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白这一路遇到店和花的顺序,有多少种不同的可能?
注意:壶里没酒 (0 斗) 时遇店是合法的,加倍后还是没酒;但是没酒时遇花是不合法的。
输入格式
第一行包含两个整数 N 和 M。
输出格式
输出一个整数表示答案。由于答案可能很大,输出模 1000000007 的结果。
数据范围
对于 40% 的评测用例:1≤N,M≤10。
对于 100% 的评测用例:1≤N,M≤100。
输入样例:
5 10
输出样例:
14
样例解释
如果我们用 0 代表遇到花,1 代表遇到店,14 种顺序如下:
010101101000000
010110010010000
011000110010000
100010110010000
011001000110000
100011000110000
100100010110000
010110100000100
011001001000100
100011001000100
100100011000100
011010000010100
100100100010100
101000001010100
//f[i,j,k]:李白一共遇到i个店,j朵花,壶里还剩k斗酒,k=1;
//f[i-1,j,k/2]+f[i,j-1,k+1]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110,MOD=1000000007;
int f[N][N][N];
int n,m;
int main()
{
cin >> n>>m;
f[0][0][2] = 1;
for(int i = 0; i <= n; i ++)
for(int j = 0; j <= m; j ++)
for(int k = 0; k <= m; k ++)
{
if(k % 2 == 0 && i >= 1) // 遇到店的情况,k只能是偶数,因为一个数乘以二不会是奇数
f[i][j][k] = (f[i][j][k] + f[i - 1][j][k / 2]) % MOD;
if(j >= 1) // 遇到花的情况,没酒时遇花是不合法的。
f[i][j][k] = (f[i][j][k] + f[i][j - 1][k + 1]) % MOD;
}
printf("%d", f[n][m - 1][1]);
return 0;
}
J 砍竹子
这天,小明在砍竹子,他面前有 n 棵竹子排成一排,一开始第 i 棵竹子的高度为 hi。
他觉得一棵一棵砍太慢了,决定使用魔法来砍竹子。
魔法可以对连续的一段相同高度的竹子使用,假设这一段竹子的高度为 HH,那么使用一次魔法可以把这一段竹子的高度都变,其中 ⌊x⌋ 表示对 x 向下取整。
小明想知道他最少使用多少次魔法可以让所有的竹子的高度都变为 1。
输入格式
第一行为一个正整数 n,表示竹子的棵数。
第二行共 n 个空格分开的正整数 hi,表示每棵竹子的高度。
输出格式
一个整数表示答案。
数据范围
对于 20% 的数据,保证 1≤n≤1000,1≤hi≤10^6。
对于 100%的数据,保证 1≤n≤2×105,1≤hi≤1018。
输入样例:
6
2 1 4 2 6 7
输出样例:
5
样例解释
其中一种方案:
2 1 4 2 6 7
→ 2 1 4 2 6 2
→ 2 1 4 2 2 2
→ 2 1 1 2 2 2
→ 1 1 1 2 2 2
→ 1 1 1 1 1 1
共需要 5 步完成。
//最大的数也只需要6次就能变成1,暴力枚举每颗竹子需要几次变成一,如果有连续的,就-1
//用栈倒着存
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 200010, M = 10;
LL f[N][M];
int n,m;//m表示层数
int main()
{
cin >> n;
LL stk[M];
int res=0;
for (int i = 0; i < n; i ++ )
{
LL x;
int top = 0;
scanf("%lld", &x);
while (x > 1) stk[ ++ top] = x, x = sqrt(x / 2 + 1);
res += top;
m = max(m, top);
for (int j = 0, k = top; k; j ++, k -- )
f[i][j] = stk[k];
}
for (int i = 0; i < m; i ++ )
for (int j = 1; j < n; j ++ )
if (f[j][i] && f[j][i] == f[j - 1][i])
res -- ;
printf("%d\n", res);
return 0;
}