第十三届蓝桥杯 C++ B组题解:

第十三届蓝桥杯 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 个单位面积):

QQ截图20220410144642.png

同时,小明有一块面积大小为 2×N 的画布,画布由 2×N个 1×1区域构成。

小明需要用以上两种积木将画布拼满,他想知道总共有多少种不同的方式?

积木可以任意旋转,且画布的方向固定。

输入格式

输入一个整数 N,表示画布大小。

输出格式

输出一个整数表示答案。

由于答案可能很大,所以输出其对 1000000007取模后的值。

数据范围

1≤N≤107。

输入样例:
3
输出样例:
5
样例解释

五种情况如下图所示,颜色只是为了标识不同的积木:

QQ截图20220410144846.png

#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 也被排除。

QQ截图20220410150120.png

#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,那么使用一次魔法可以把这一段竹子的高度都变image-20220503111425590,其中 ⌊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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要AC呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值