第十一届蓝桥杯省赛第一场C++ A/B组 真题题解(详细讲解+代码分析)看这篇就够了~~~

第十一届蓝桥杯省赛第一场C++ A/B组 真题题解

整除序列

题目描述
有一个序列,序列的第一个数是 n,后面的每个数是前一个数整除 2,请输出这个序列中值为正数的项。

输入格式
输入一行包含一个整数 n。

输出格式
输出一行,包含多个整数,相邻的整数之间用一个空格分隔,表示答案。

数据范围
1≤n≤1018
输入样例:

20

输出样例:

20 10 5 2 1

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll n;
    cin>>n;
    while(n>0)
    {
        cout<<n<<" ";
        n>>=1;
    }
    return 0;
}

解码

题目描述
小明有一串很长的英文字母,可能包含大写和小写。
在这串字母中,有很多连续的是重复的。
小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母 + 出现次数的形式。
例如,连续的 5 个 a,即 aaaaa,小明可以简写成 a5(也可能简写成 a4a、aa3a 等)。
对于这个例子:HHHellllloo,小明可以简写成 H3el5o2。
为了方便表达,小明不会将连续的超过 9 个相同的字符写成简写的形式。
现在给出简写后的字符串,请帮助小明还原成原来的串。

输入格式
输入一行包含一个字符串。

输出格式
输出一个字符串,表示还原后的串。

数据范围
输入字符串由大小写英文字母和数字组成,长度不超过 100。
请注意原来的串长度可能超过 100。

输入样例:

H3el5o2

输出样例:

HHHellllloo

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]>='0'&&s[i]<='9')
        {
            int t=s[i]-'0'-1;
            while(t--)
            {
                cout<<s[i-1];
            }
        }
        else cout<<s[i];
    }
    return 0;
}

走方格

题目描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。
只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。

输入格式
输入一行包含两个整数 n,m。

输出格式
输出一个整数,表示答案。

数据范围
1≤n,m≤30
输入样例1:

3 4

输出样例1:

2

输入样例2:

6 6

输出样例2:

0

思路

动态规划
f[i][j] 表示从点 (1,1) 走到点 (n,m) 的方案数。
如果 i,j都是偶数,那么特判为 0。
否则只能从上边或者左边转移过来,即f[i][j] = f[i - 1][j] + f[i][j - 1]
边界情况:f[i][1] = f[1][j] = 1

AC代码

#include<bits/stdc++.h>
using namespace std;
int f[40][40];
int main()
{
    int n,m;
    cin>>n>>m;
    f[1][1]=1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        if(i==1&&j==1) continue;
        if(i%2||j%2) f[i][j]=f[i-1][j]+f[i][j-1];
    }
    cout<<f[n][m]<<endl;
    return 0;
}

整数拼接

题目描述
给定一个长度为 n 的数组 A1,A2,⋅⋅⋅,An。
你可以从中选出两个数 Ai 和 Aj(i 不等于 j),然后将 Ai 和 Aj 一前一后拼成一个新的整数。
例如 12 和 345 可以拼成 12345 或 34512。
注意交换 Ai 和 Aj 的顺序总是被视为 2 种拼法,即便是 Ai=Aj 时。
请你计算有多少种拼法满足拼出的整数是 K 的倍数。

输入格式
第一行包含 2 个整数 n 和 K。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An。

输出格式
一个整数代表答案。

数据范围
1≤n≤105,
1≤K≤105,
1≤Ai≤109
输入样例:

4 2
1 2 3 4

输出样例:

6

思路

拼接两个整数比如 12345 ,得出的 12345 就等于12 ✖ 10 ^ 3 + 345,34512等于345 ✖ 10 ^ 2 + 12。
因此本题就相当于求Ai和Aj其(Ai + Aj * 10 ^ len(Ai)) % k = 0这一等式,其中len(Ai)是Ai的位数。
将上述等式转化为 (Aj * 10 ^ len(Ai)) %k =Ai % k
因此本题就成了首先枚举  Ai ,然后求有几个 Aj 其乘以10的 len(Ai)次方 % k = Ai % k。
因此,创建一个二维数组s[i][j],这个数组就表示 (某个数 ✖ 10 ^ i)% k = j 的数量
所以本题就转换为每次枚举一个Ai,然后计算出其%k的数来,然后再算出Ai的位数len,然后去寻找 s[len][Ai % k] 的大小,
就能得知有多少个数满足条件,同时也就是答案数。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int s[11][N];//表示某个数*10^i%k==j的数量
ll a[N];//存放n个数
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++)
        cin>>a[i];
    ll res=0;
     //预处理s数组
    for(int i=0;i<n;i ++)
    {
        ll t=a[i]%k;
        for(int j=0;j<11;j++)//因为题目中给出的最大数是10^9
        {
            s[j][t]++;
            t=t*10%k;
        }            
    }
    //循环数组计算答案
    for(int i=0;i<n;i++)
    {
        ll t=a[i]%k;
        int len=to_string(a[i]).size();//将这个数字转化为字符串,再判断转换后的字符串的位数就等于这个数字本身的位数
        res+=s[len][(k - t) % k];
        //判重
        ll x=t;
        while(len--) x=x*10%k; //等价于求a[i]乘以10^len的余数,同上面的预处理求法一样
        if(x==(k - t)%k) res--;
    }
    cout << res << endl;
    return 0;
}

网络分析

题目描述
小明正在做一个网络实验。
他设置了 n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。
两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。
所有发送和接收的节点都会将信息存储下来。
一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。

输入格式
输入的第一行包含两个整数 n,m,分别表示节点数量和操作数量。
节点从 1 至 n 编号。
接下来 m 行,每行三个整数,表示一个操作。
如果操作为 1 a b,表示将节点 a 和节点 b 通过网线连接起来。当 a = b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t,表示在节点 p 上发送一条大小为 t 的信息。
输出格式
输出一行,包含 n 个整数,相邻整数之间用一个空格分割,依次表示进行完上述操作后节点 1 至节点 n 上存储信息的大小。

数据范围
1≤n≤10000,
1≤m≤105,
1≤t≤100
输入样例1:

4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1

输出样例1:

13 13 5 3

思路

并查集 树上差分
我们通过并查集合并连通块,保证同一个连通块内的点同属一个集合
对于每一个合并操作,找到两个点所属的集合
如果这两个点不在同一连通块,那么我们构造一个新点,使这个新点成为集合合并后的根节点
这样进行 k次有效合并操作后,就会产生 k 个新点
我们所构造的图是若干棵树,编号为 1−n的节点都是树的叶子节点
对于每次连通块累加操作,我们只需要向集合的根节点累加一个值即可
最后对我们所构造出来的一堆树DP(只是遍历一下),把每个点的权值下放到子树中的所有节点中
然后依次输出编号为 1−n的节点的权值即可

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=N/2;
int h[N],e[M],ne[M],p[N],f[N],n,m,idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int find(int x)
{
    if(p[x]!=x)  p[x]=find(p[x]);
    return p[x];
}
void dfs(int u, int fa)
{
    f[u]+=f[fa];
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        dfs(j,u);
    }
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n*2;i ++) p[i]=i;
    int r=n+1;
    while(m--)
    {
        int op, a, b;
       cin>>op>>a>>b;
        if(op==1)
        {
            a=find(a),b=find(b);
            if(a!=b)
            {
                p[a]=p[b]=r;
                add(r,a);
                add(r,b);
                r++;
            }
        }
        else
        {
            a=find(a);
            f[a]+=b;
        }
    }
    for(int i=n+1;i<r;i++)
    {
      if(p[i]==i) dfs(i, 0);
    }
    for(int i = 1; i <= n; i ++)
    {
      cout<<f[i]<<' ';
    }
    return 0;
}

超级胶水

题目描述
小明有 n 颗石子,按顺序摆成一排。
他准备用胶水将这些石子粘在一起。
每颗石子有自己的重量,如果将两颗石子粘在一起,将合并成一颗新的石子,重量是这两颗石子的重量之和。
为了保证石子粘贴牢固,粘贴两颗石子所需要的胶水与两颗石子的重量乘积成正比,本题不考虑物理单位,认为所需要的胶水在数值上等于两颗石子重量的乘积。
每次合并,小明只能合并位置相邻的两颗石子,并将合并出的新石子放在原来的位置。
现在,小明想用最少的胶水将所有石子粘在一起,请帮助小明计算最少需要多少胶水。

输入格式
输入的第一行包含一个整数 n,表示初始时的石子数量。
第二行包含 n 个整数 w1,w2,…,wn,依次表示每颗石子的重量。

输出格式
一个整数表示答案。

数据范围
1≤n≤105,
1≤wi≤1000
输入样例1:

3
3 4 5

输出样例1:

47

输入样例2:

8
1 5 2 6 3 7 4 8

输出样例2:

546

思路

当n=2时:ab
当n=3时:ab+(a+b)c=ac+(a+c)b=bc+(b+c)a=ab+ac + bc
当n=4时:ab+(a+b)c+(a+b+c)d=ac+(a+c)b+(a+c+b)d=.....=ab+ac+ad+ bc+bd +cd
以此类推 每个数分别与后面的所有数相乘之和即可
也就等于
当n=2时: ab
当n=3时:ab+ac+bc=ab+(a+b)c
当n=4时:ab+ac+ad+bc+bd+cd=ab+(a+b)c+(a+b+c)*d

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int n,x;
    cin>>n;
    ll sum=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        sum+=ans*x;
        ans+=x;
    }
    cout<<sum<<endl;
    return 0;
}

如果觉得写的还不错,那就一键三连吧 ^ v ^

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

稚皓君

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

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

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

打赏作者

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

抵扣说明:

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

余额充值