2020十一届蓝桥杯B 第一场 C/C++

题目A:跑步训练

本题总分:5 分
【问题描述】
小明要做一个跑步训练。
初始时,小明充满体力,体力值计为 10000 。如果小明跑步,每分钟损耗 600 的体力。如果小明休息,每分钟增加 300 的体力。体力的损耗和增加都是均匀变化的。
小明打算跑一分钟、休息一分钟、再跑一分钟、再休息一分钟……如此循环。如果某个时刻小明的体力到达 0 ,他就停止锻炼。
请问小明在多久后停止锻炼。为了使答案为整数,请以秒为单位输出答案。答案中只填写数,不填写单位。

#include<bits/stdc++.h>
int solve() {
    
    
  int x = 10000, res = 0;
  while(1) {
    
    
    for(int i=0;i<60;i++) {
    
    
      x -= 10; ++res;     
      if(x <= 0) return res;
    }
    x += 300; res += 60;
  }
}
int main() {
    
    
  cout << solve();
  return 0;
}

题目B:纪念日

本题总分:5 分
【问题描述】
2020 年 7 月 1 日是中国 共 产 党 成立 99 周年纪念日。
中国 共 产 党 成立于 1921 年 7 月 23 日。
请问从 1921 年 7 月 23 日中午 12 时到 2020 年 7 月 1 日中午 12 时一共包含多少分钟?

电脑计算机计算

在这里插入图片描述

在这里插入图片描述

题目C:合并检测

本题总分:10 分
【问题描述】
新冠疫情由新冠病毒引起,最近在 A 国蔓延,为了尽快控制疫情, A 国准备给大量民众进病毒核酸检测。
然而,用于检测的试剂盒紧缺。
为了解决这一困难,科学家想了一个办法:合并检测。即将从多个人( k 个)采集的标本放到同一个试剂盒中进行检测。如果结果为阴性,则说明这 k 个人都是阴性,用一个试剂盒完成了 k 个人的检测。如果结果为阳性,则说明至少有一个人为阳性,需要将这 k 个人的样本全部重新独立检测(从理论上看,如果检测前 k−1 个人都是阴性可以推断出第 k 个人是阳性,但是在实际操作中不会利用此推断,而是将 k 个人独立检测),加上最开始的合并检测,一共使用了 k+1 个试剂盒完成了 k 个人的检测。
A 国估计被测的民众的感染率大概是 1,呈均匀分布。请问 k 取多少能最节省试剂盒?

设A国共有100人,那么合并检测需要用的试剂为 100/k 个,均匀分布可以认为100个人里面就有一个人感染,所以,对于这一个人还需要个试剂,结果就是 100 /k + k ,根据基本不等式,当 100/ k = k 时,等式取到最小值,这时k=10。

题目D:REPEAT程序

附件 prog.txt 中是一个用某种语言写的程序。
其中 REPEAT k 表示一个次数为 k 的循环。循环控制的范围由缩进表达,从次行开始连续的缩进比该行多的(前面的空白更长的)为循环包含的内容。
例如如下片段:
REPEAT 2:
 A = A + 4
 REPEAT 5:
  REPEAT 6:
   A = A + 5
  A = A + 7
 A = A + 8
A = A + 9
该片段中从 A = A + 4 所在的行到 A = A + 8 所在的行都在第一行的循环两次中。
REPEAT 6: 所在的行到 A = A + 7 所在的行都在 REPEAT 5: 循环中。
A = A + 5 实际总共的循环次数是 2 × 5 × 6 = 60 次。
请问该程序执行完毕之后,A 的值是多少?

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 2020;
char s[MAXN];
// a[i] -> 第 i 层循环的缩进,b[i] -> 第 i 层循环的循环次数
int a[MAXN], b[MAXN];
int main() {
    
    
    freopen("prog.txt", "r", stdin);
    int pos = 0, ans = 0, w = 1;
    gets(s); // 读走第一行的 A = 0
    a[pos] = -1, b[pos] = 1; // 防止在栈空的时候弹栈
    while (gets(s)) {
    
    
        int n = strlen(s), p = 0;
        while (s[p] == ' ') p++; // 统计缩进
        while (a[pos] >= p) w /= b[pos--];// 弹掉栈里缩进大于等于当前行的
        if (s[n - 1] == ':') {
    
     // 当前行是循环,压栈
            int k = s[n - 2] - '0';
            pos = pos + 1;
            w *= k;
            a[pos] = p, b[pos] = k;
        } else {
    
    
            int k = s[n - 1] - '0';
            ans += k * w;
        }
    }
    printf("%d\n", ans);
    return 0;
}

或者看成python程序也可计算
在这里插入图片描述
在这里插入图片描述

题目E:矩阵

本题总分:15 分
【问题描述】
把 1∼2020 放在 2×1010 的矩阵里。要求同一行中右边的比左边大,同一列中下边的比上边的大。一共有多少种方案?
答案很大,你只需要给出方案数除以 2020 的余数即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 2030;
const int MOD = 2020;
int dp[MAXN][MAXN];
int main() {
    
    
    int n = 2020;
    dp[1][1] = 1; // 1必然放在第一行
    for (int i = 2; i <= n; i++) {
    
    
        for (int j = 1; j <= i; j++) {
    
    
            dp[i][j] += dp[i - 1][j - 1]; // 将第i个数放第一行
            if (i - j <= j) {
    
    
                /*
                  因为是正向枚举,后面的数只会越来越大
                  要随时保持第一行的个数不能比第二行的少
                  否则必然出现这一列第二行比第一行小的情况
                */
                dp[i][j] += dp[i - 1][j];
            }
            dp[i][j] %= MOD;
        }
    }
    printf("%d\n", dp[2020][1010]);
    return 0;
}

题目F:整数序列

时间限制: 1.0s  内存限制: 256.0MB  本题总分:15 分
【问题描述】
有一个序列,序列的第一个数是 n,后面的每个数是前一个数整除 2,请输出这个序列中值为正数的项。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一行,包含多个整数,相邻的整数之间用一个空格分隔,表示答案。
【评测用例规模与约定】
对于 80% 的评测用例,1≤n≤109。
对于所有评测用例,1≤n≤1018。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int main() {
    
    
    long long n;
    for (cin >> n; n != 0; n >>= 1) {
    
    
        cout << n;
        if (n != 1) putchar(' ');
        else putchar('\n');
    }
    return 0;
}

题目G:解码

时间限制: 1.0s  内存限制: 256.0MB  本题总分:20 分
【问题描述】
小明有一串很长的英文字母,可能包含大写和小写。
在这串字母中,有很多连续的是重复的。小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母 + 出现次数的形式。
例如,连续的 $5 个 a,即 aaaaa,小明可以简写成 a5(也可能简写成 a4a、aa3a 等)。对于这个例子:HHHellllloo,小明可以简写成 H3el5o2。为了方便表
达,小明不会将连续的超过 9 个相同的字符写成简写的形式。
现在给出简写后的字符串,请帮助小明还原成原来的串。
【输入格式】
输入一行包含一个字符串。
【输出格式】
输出一个字符串,表示还原后的串。
【评测用例规模与约定】
对于所有评测用例,字符串由大小写英文字母和数字组成,长度不超过 100。
请注意原来的串长度可能超过 100。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 110;
char s[MAXN];
int main() {
    
    
    scanf("%s", s);
    for (int i = 0; s[i]; i++) {
    
    
        if (s[i] >= 'a' && s[i] <= 'z') {
    
    
            putchar(s[i]);
        } else if (s[i] >= 'A' && s[i] <= 'Z') {
    
    
            putchar(s[i]);
        } else {
    
    
            int k = s[i] - '0' - 1;
            while (k--) putchar(s[i - 1]);
        }
    }
    puts("");
    return 0;
}

题目H:走方格

时间限制: 1.0s  内存限制: 256.0MB  本题总分:20 分
【问题描述】
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
【输入格式】
输入一行包含两个整数 n, m。
【输出格式】
输出一个整数,表示答案。
【评测用例规模与约定】
对于所有评测用例,1≤n≤30,1≤m≤30。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 40;
int dp[MAXN][MAXN];
int main() {
    
    
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
    
    
        for (int j = 1; j <= m; j++) {
    
    
            if (i == 1 && j == 1) {
    
    
                dp[i][j] = 1;
                continue;
            }
            if ((i & 1) || (j & 1)) {
    
    
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    }
    printf("%d\n", dp[n][m]);
    return 0;
}

题目I:整数拼接

时间限制: 1.0s  内存限制: 256.0MB  本题总分:25 分
【问题描述】
给定义个长度为 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。
【输出格式】
一个整数代表答案。
【评测用例规模与约定】
对于 30% 的评测用例,1≤n≤1000,1≤K≤20,1≤Ai≤104。
对于所有评测用例,1≤n≤105,1≤K≤105,1≤Ai≤109。

做法:正解就是把所有数扩大几倍后存起来,再用map找,要理解到,对于这些存起来的数字取膜k以后,只有两个数字加起来%k等于0才算一对,后面就能暴力了。用数组存每个数扩大10倍、100倍…十的十次方倍,存入a数组,再用一个vis充当map的作用,顺势记录这个数有多少位。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL a[N][11], x, mid;
int vis[N][11], n, k;
int cw(LL x) {
    
    
	int res = 0;
	while(x) {
    
    
		++res; x /= 10;
	}
	return res;
}
int main() {
    
    
	scanf("%d%d", &n, &k);
	for(int i = 0; i < n; ++i) {
    
    
		scanf("%lld", &x);
		++vis[x%k][cw(x)]; a[i][0] = x;
		for(int j = 1; j <= 10; ++j) a[i][j] = a[i][j-1] * 10 % k;
	}
	LL res = 0;
	for(int i = 0; i < n; ++i) {
    
    
		for(int j = 1; j <= 10; ++j) {
    
    
			mid = k - a[i][j]; mid %= k;
			if(vis[mid][j]) {
    
    
				res += vis[mid][j];
				if(a[i][0]%k == mid && cw(a[i][0]) == j) --res;
			}
		}
	}
	
	printf("%lld\n", res);
	return 0;
}

题目J:网络分析

时间限制: 1.0s  内存限制: 256.0MB  本题总分:25 分
【问题描述】
小明正在做一个网络实验。
他设置了 n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。所有发送和接收的节点都会将信息存储下来。一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。
【输入格式】
输入的第一行包含两个整数 n,m,分别表示节点数量和操作数量。节点从
1 至 n 编号。
接下来 m 行,每行三个整数,表示一个操作。
如果操作为 1 a b,表示将节点 a 和节点 b 通过网线连接起来。当 a=b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t,表示在节点 p 上发送一条大小为 t 的信息。
【输出格式】
输出一行,包含 n 个整数,相邻整数之间用一个空格分割,依次表示进行
完上述操作后节点 1 至节点 n 上存储信息的大小。
【评测用例规模与约定】
对于 30% 的评测用例,1≤n≤20,1≤m≤100。
对于 50% 的评测用例,1≤n≤100,1≤m≤1000。
对于 70% 的评测用例,1≤n≤1000,1≤m≤10000。
对于所有评测用例,1≤n≤10000,1≤m≤100000,1≤t≤100。

做法:用w数组记录该点所有子孙都需要+的权值,每次合并都下传标记,复杂度n^2,极限卡常

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
int w[N], fa[N], res[N];
int Find(int x) {
    
    
  return fa[x] == x ? x : fa[x] = Find(fa[x]);
} 
int n, m;
void Merg(int x, int y) {
    
    
  int fx = Find(x), fy = Find(y);
  if(fx != fy) {
    
    
    for(register int i = 1; i <= n; ++i) res[i] += w[Find(i)];
    memset(w, 0, sizeof w);
    fa[fx] = fy;
  }
  return;
}
int main() {
    
    
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; ++i) fa[i] = i;
  int opt, a, b;
  while(m--) {
    
    
    scanf("%d%d%d", &opt, &a, &b);
    if(opt == 1) Merg(a, b);
    else w[Find(a)] += b;
  }
  for(int i = 1; i <= n; ++i) printf("%d%c", res[i]+w[Find(i)], i==n?'\n':' ');
  return 0;
} 

连接两个连通块,很容易想到并查集,但是比赛的时候没有想到如何比较好的解决整个连通块加上一个 t,所以就暴力枚举所有节点,如果和 p 属于一个连通块就加 t,对标70分的做法。赛后好像想明白怎么解决整个连通块的修改了,还是类似线段树懒标记先把修改存到连通块的根上面之后再往下传递。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAXN = 10010;
int dsu[MAXN], mark[MAXN];
int find(int u) {
    
    
    if (dsu[u] == 0) return u;
    int fu = find(dsu[u]);
    if (dsu[u] != fu) {
    
     // 没有直接连在根上
        mark[u] += mark[dsu[u]]; // 把父亲那的数据懒标记下传
        dsu[u] = fu; // 把根设为父亲, 状态压缩
    }
    return fu;
}
int main() {
    
    
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
    
    
        int op, a, b;
        scanf("%d%d%d", &op, &a, &b);
        if (op == 1) {
    
    
            int fa = find(a);
            int fb = find(b);
            if (fa != fb) {
    
    
                dsu[fa] = fb;
                /*
                由于fa接到了fb上,fb的mark之后会传递给fa
                但是这部分数据是fb独有的,不该传给fa
                所以事先在fa里减掉一个mark[fb]
                之后mark[fb]传回来才能保持不受影响
                */
                mark[fa] -= mark[fb];
            }
        } else {
    
    
            int fa = find(a);
            mark[fa] += b;
        }
    }
    for (int i = 1; i <= n; i++) {
    
    
        int res = mark[i];
        int fi = find(i);
        if (fi != i) res += mark[fi]; // 自己不是根,就说明有部分数据在根上还没传下来
        printf("%d%c", res, " \n"[i == n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43530438/article/details/109112861