添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

第一次接触写国赛的题,在下才疏学浅,题解如有错误请指正。🤗
A ~ H有题解,其中E题作者打的暴力。
如果能帮助你的话,点点赞吧!谢谢🤝

试题 A: 子 2023

本题总分:5 分

【问题描述】
小蓝在黑板上连续写下从 1 到 2023 之间所有的整数,得到了一个数字序列:
S = 12345678910111213 . . . 20222023。
小蓝想知道 S 中有多少种子序列恰好等于 2023?
提示,以下是 3 种满足条件的子序列(用中括号标识出的数字是子序列包含的数字):
1[2]34567891[0]111[2]1[3]14151617181920212223…
1[2]34567891[0]111[2]131415161718192021222[3]…
1[2]34567891[0]111213141516171819[2]021222[3]…
注意以下是不满足条件的子序列,虽然包含了 2、0、2、3 四个数字,但是顺序不对:
1[2]345678910111[2]131415161718192[0]21222[3]…

【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分

作完省赛的就知道,这不就是省赛E题的接龙数列嘛😅。简单的dp就可以。分别以2 0 2 3(要转化,因为有两个2)结尾dp就可以了👻。
虽然正解是dp,但是暴力 + 剪枝也可以做,就是跑的有些慢。作者本地跑差不多50s。
还一个 ,就是如果你不开long long就会得到错的答案: 1189693313

#include<bits/stdc++.h>
using namespace std;
#define int long long // 开long long 
string s; 
int f1() {
	 int cnt = 0;
 	for (int a = 0; a < s.size(); a++) {
 		if (s[a] != '2')	continue;
 		for (int b = a + 1; b < s.size(); b++) {
 			if (s[b] != '0')	continue;
 			for (int c = b + 1; c < s.size(); c++) {
 				if (s[c] != '2')	continue;
 				for (int d = c + 1; d < s.size(); d++) {
 					if (s[d] != '3')	continue;
 					cnt++;
 	return cnt;
int f2() {
	int dp[4] = {0}; // 分别代表"2"、"20"、"202"、"2023"的数量
	for (char ch : s) {
		if (ch == '2') {
			dp[0]++;
			dp[2] += dp[1];
		if (ch == '0') dp[1] += dp[0];
		if (ch == '3') dp[3] += dp[2];
	return dp[3];
signed main(){
	for (int i = 1; i <= 2023; i++) { // 剪枝 
		string tmp = to_string(i);
		for (char ch : tmp) {
			if (ch == '2') s += '2';
			if (ch == '0') s += '0';
			if (ch == '3') s += '3';
//	cout << f1() << endl; // 暴力 
	cout << f2() << endl; // dp
	return 0;
5484660609

试题 B: 双子数

本题总分:5 分

【问题描述】
  若一个正整数 x 可以被表示为 p 2,其中 p、q 为质数且 p , q,则 x 是一个 “双子数”。请计算区间 [2333, 23333333333333] 内有多少个 “双子数”?

【答案提交】
  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

首先第一眼看到质数,那么直接就是质数筛嘛。筛多少质数呢?看区间数量级是10 void euler_prime(int n) { for (int i = 2; i <= n; i++) { if (!flag[i]) prime.push_back(i); for (int p : prime) { flag[p * i] = 1; if (i % p == 0 || p * i > n) break; signed main () { euler_prime(5e6 + 7); for (int i = 0; i < prime.size(); i++) { for (int j = i + 1; j < prime.size(); j++) { int x = prime[i] * prime[j]; // 这里的x是题目中的x开根号 if (x < pow(l, 0.5)) continue; if (x > pow(r, 0.5)) break; // 太大后面的数都不用看了,都大于范围 ans++; cout << ans << endl; return 0;

试题 C: 班级活动

时间限制: 1.0s 内存限制: 256.0MB 本题总分:10 分

【问题描述】
  小明的老师准备组织一次班级活动。班上一共有 n 名(n 为偶数)同学,老师想把所有的同学进行分组,每两名同学一组。为了公平,老师给每名同学随机分配了一个 n 以内的正整数作为 id,第 i 名同学的 id 为 a

结论: 配对超过2个同学的数字总和,设为cnt1, 没有完成配对的同学的数字总和,设为cnt2,
当cnt1 > cnt2时,输出cnt1
当cnt1 < cnt2时,输出cnt1 + (cnt2 - cnt1) / 2
时间复杂度:O(n)

#include <bits/stdc++.h>
using namespace std;
const int maxl = 1e5 + 7;
int n, cnt1, cnt2;
int a[maxl];
map<int,int> mp; 
bool flag[maxl];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		mp[a[i]]++;
	for (int i = 1; i <= n; i++) {
		if (!flag[a[i]]) {
			if (mp[a[i]] == 1) cnt2++;
			else if (mp[a[i]] > 2) cnt1 += (mp[a[i]] - 2);
			flag[a[i]] = 1;
	if (cnt1 > cnt2) cout << cnt1 << endl;
	else cout << cnt1 + (cnt2 - cnt1) / 2 << endl;
	return 0;

试题 D: 合并数列

时间限制: 1.0s 内存限制: 256.0MB 本题总分:10 分

【问题描述】
  小明发现有很多方案可以把一个很大的正整数拆成若干正整数的和。他采取了其中两种方案,分别将他们列为两个数组 {a

作者个人觉得不会这道题的人,大概率是没看到题目中的,相邻的两个数 。这里我给的大家标出来了。
看到这就应该是迎刃而解了,贪心就可以了。直接放代码了,我觉得你一定看的懂的!🤗
时间复杂度:O(n)

#include<bits/stdc++.h>
using namespace std;
int n, m, ans;
int tp1, tp2;
queue<int> q1, q2;
int main(){
	cin >> n >> m;
	for (int i = 1, x; i <= n; i++) cin >> x, q1.push(x);
	for (int i = 1, x; i <= m; i++) cin >> x, q2.push(x);
	while (!q1.empty()) {
		tp1 = q1.front();
		tp2 = q2.front();
		if (tp1 == tp2) q1.pop(), q2.pop();
		else if (tp1 < tp2) q1.pop(), q1.front() += tp1, ans++;
		else q2.pop(), q2.front() += tp2, ans++;
	cout << ans << endl;
    return 0;

试题 E: 数三角

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分

【问题描述】
小明在二维坐标系中放置了 n 个点,他想在其中选出一个包含三个点的子集,这三个点能组成三角形。然而这样的方案太多了,他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选法可以组成等腰三角形?

【输入格式】
输入共 n + 1 行。
第一行为一个正整数 n。
后面 n 行,每行两个整数 x double dis(int a, int b) { return sqrt((p[a].x - p[b].x) * (p[a].x - p[b].x) + (p[a].y - p[b].y) * (p[a].y - p[b].y)) * 1.00; signed main(){ cin >> n; for (int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y; // 遍历所有点,并且要避免重复 for (int i = 1; i <= n; i++) { for (int j = 1; j < i; j++) { for (int k = 1; k < j; k++) { double a = dis(i, j); double b = dis(i, k); double c = dis(j, k); if (a + b > c && a + c > b && b + c > a) if ((a == b) || (a == c) || (b == c)) ans++; cout << ans << endl; return 0;

试题 F: 删边问题

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分

【问题描述】
给定一个包含 N 个结点 M 条边的无向图 G,结点编号 1 . . . N。其中每个
结点都有一个点权 W i
你可以从 M 条边中任选恰好一条边删除,如果剩下的图恰好包含 2 个连通
分量,就称这是一种合法的删除方案。
对于一种合法的删除方案,我们假设 2 个连通分量包含的点的权值之和分
别为 X 和 Y,请你找出一种使得 X 与 Y 的差值最小的方案。输出 X 与 Y 的差
值。

【输入格式】
第一行包含两个整数 N 和 M。
第二行包含 N 个整数,W N
以下 M 行每行包含 2 个整数 U 和 V,代表结点 U 和 V 之间有一条边。

【输出格式】
一个整数代表最小的差值。如果不存在合法的删除方案,输出 −1。

【样例输入】

10 20 30 40

【样例输出】

【样例说明】
由于 1 和 2 之间实际有 2 条边,所以合法的删除方案有 2 种,分别是删除(2, 3) 之间的边和删除 (3, 4) 之间的边。
删除 (2, 3) 之间的边,剩下的图包含 2 个连通分量:{1, 2} 和 {3, 4},点权和分别是 30、70,差为 40。
删除 (3, 4) 之间的边,剩下的图包含 2 个连通分量:{1, 2, 3} 和 {4},点权和分别是 60、40,差为 20。

【评测用例规模与约定】
对于 20% 的数据,1 ≤ N, M ≤ 10000。
对于另外 20% 的数据,每个结点的度数不超过 2。
对于 100% 的数据,1 ≤ N, M ≤ 200000,0 ≤ W i ≤ 109,1 ≤ U, V ≤ N。

最近马上国赛了,看了看自己写的这题发现是错误的。特此改正😶
这题就是tarjan算法求割边
割边: 对于一个无向图,如果删掉一条边后图中的连通块个数增加了,则称这条边为桥或者割边

简单来说,删掉割边,一个图就会变成两个连通子图

主体情况,枚举割边,计算两边的连通子图的差值(这里算一个子图权值和就行,另一个通过总权值和减去当前这个子图值和)

特殊情况:

  1. 没有割边
  2. 原来的图本来就是两个连通分量(直接给出答案就行)

题目没有说不连通的情况只有两个连通分量,这点如果硬要考虑的话还要加判断,但是这个代码就可以在蓝桥杯上拿满了。🥳

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define P pair<int, int>
#define x first
#define y second
const int maxl = 1e6 + 7;
const int maxn = 0x3f3f3f3f3f3f3f3f;
int n, m, ans = maxn;
vector<int> G[maxl];
int w[maxl], sum_w;
int dfn[maxl], low[maxl], dfn_cnt;
void tarjan(int u, int fnt) {
	dfn[u] = low[u] = ++dfn_cnt;
	for (int v : G[u]) {
		if (!dfn[v]) {
			tarjan(v, u);
			w[u] += w[v];
			low[u] = min(low[u], low[v]);
			if (low[v] > dfn[u]) ans = min(ans, llabs(sum_w - 2 * w[v]));
		} else if (dfn[v] < dfn[u] && v != fnt) low[u] = min(low[u], dfn[v]);
void slove() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> w[i], sum_w += w[i];
	for (int i = 1, u, v; i <= m; i++) {
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	tarjan(1, 0);
	if (w[1] != sum_w) cout << sum_w - 2 * w[1] << endl;
	else if (ans != maxn) cout << ans << endl;
	else cout << -1 << endl;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    /*cin >> t;*/
    while (t--) slove();
    return 0;

试题 G: AB 路线

时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分

【问题描述】
  有一个由 N × M 个方格组成的迷宫,每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格,目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。
  由于特殊的原因,小蓝的路线必须先走 K 个 A 格子、再走 K 个 B 格子、再走 K 个 A 格子、再走 K 个 B 格子……如此反复交替。
  请你计算小蓝最少需要走多少步,才能到达右下角方格?
  注意路线经过的格子数不必一定是 K 的倍数,即最后一段 A 或 B 的格子可以不满 K 个。起点保证是 A 格子。

例如 K = 3 时,以下 3 种路线是合法的:
AA
AAAB
AAABBBAAABBB
以下 3 种路线不合法:
ABABAB
ABBBAAABBB
AAABBBBBBAAA

【输入格式】
第一行包含三个整数 N、M 和 K。
以下 N 行,每行包含 M 个字符(A 或 B),代表格子类型。

【输出格式】
一个整数,代表最少步数。如果无法到达右下角,输出 −1。

【样例输入】

4 4 2

【样例输出】

【样例说明】
每一步方向如下:下右下右上右下下;路线序列:AABBAABBA。

【评测用例规模与约定】
对于 20% 的数据,1 ≤ N, M ≤ 4。
对于另 20% 的数据,K = 1。
对于 100% 的数据,1 ≤ N, M ≤ 1000,1 ≤ K ≤ 10。

当年写这道题可能还是大意了,代码没过,没有判重,被hack掉了,这里感谢网友指正,现在已经改正了。🥰
这种题dfs遍历的题,做过板子题走迷宫后,其实都大差不差。难点主要是在每走一步状态的定义上。
与走迷宫对比,都是走到终点,最小步数。
条件:

  • 走迷宫的状态是每条路走一次
  • 这道题是交替走k步

也就是把交替k步描述出来就可以了。这里看代码怎么写的吧!
做出来这题了,也可以做做2024 cb组省赛 数字接龙。也都是状态标记定义的好题。💕

#include<bits/stdc++.h>
#include <queue>
using namespace std;
/*#define int long long*/
#define endl '\n'
#define P pair<int, int>
#define x first
#define y second
const int maxl = 1e3 + 7;
const int maxn = 0x3f3f3f3f;
const int N = 1e1 + 7;
int dx[] = {0, 0, 0, 1, -1};
int dy[] = {0, 1, -1, 0, 0};
struct node {
    int x;
    int y;
    int step;
int n, m, k;
char G[maxl][maxl];
bool vis[maxl][maxl][N];
int dis[maxl][maxl][N];
queue<node> q;
void slove() {
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> G[i][j];
    vis[1][1][1] = 1;
    q.push({1, 1, 1});
    while (!q.empty()) {
        node tp = q.front();
        q.pop();
        for (int i = 1; i <= 4; i++) {
            int x = tp.x + dx[i];
            int y = tp.y + dy[i];
            if (x < 1 || x > n || y < 1 || y > m) continue; // 地图边界判断
            // [1, k]走了k步, 所以第k + 1步要进行变号(注意这里用的是tp.step)
            if (G[x][y] == G[tp.x][tp.y] && tp.step == k) continue; // 走了k + 1步, 没有变号
            if (G[x][y] != G[tp.x][tp.y] && tp.step != k) continue; // 没走k + 1步,变号了
            int step = (tp.step) % k + 1;
            if (vis[x][y][step]) continue; // 这个点走过了第step步
            vis[x][y][step] = 1;
            dis[x][y][step] = dis[tp.x][tp.y][tp.step] + 1;
            q.push({x, y, step});
    int ans = maxn;
    for (int i = 1; i <= k; i++) {
        if (vis[n][m][i]) {
            ans = min(ans, dis[n][m][i]); // 到达终点的可能步数全都遍历一遍
    if (ans == maxn) cout << -1 << endl;
    else cout << ans << endl;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    /*cin >> t;*/
    while(t--) slove();
    return 0;

试题 H: 抓娃娃

时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分

【问题描述】
  小明拿了 n 条线段练习抓娃娃。他将所有线段铺在数轴上,第 i 条线段的左端点在 l

我只能说,这道题是真的简单。而且是20分啊?!!为啥不放到最前面10分啊?真服了😡
不就是,最基础的前缀和与差分嘛!
抓住以下几个点:

  1. 框住区间中间,就算抓住
  2. 会有精度问题。乘2防止
#include<bits/stdc++.h>
using namespace std;
const int maxl = 2e6 + 7;
int n, m;
int sum[maxl]; 
int main(){
	cin >> n >> m;
	for (int i = 1, l, r; i <= n; i++) {
		cin >> l >> r;
		sum[l + r]++;
	for (int i = 1; i < maxl; i++) sum[i] += sum[i - 1];
	for (int i = 1, l, r; i <= m; i++) {
		cin >> l >> r;
		l *= 2;
		r *= 2;
		cout << sum[r] - sum[l - 1] << endl;
	return 0;
 

后面两道题,作者能力有限不会。如果你们能会的话。记得留言告诉我。在下把题目放在这里了。

试题 I: 拼数字

时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分

【问题描述】
小蓝要用 N 个数字 2 和 M 个数字 3 拼出一个 N + M 位的整数。请你计算
小蓝能拼出的最大的 2023 的倍数是多少?

【输入格式】
两个整数 N 和 M。

【输出格式】
一个 N + M 位的整数,代表答案。如果拼不出 2023 的倍数,输出 −1。

【样例输入】

【样例输出】

2233333333

【评测用例规模与约定】
对于 20% 的数据,1 ≤ N, M ≤ 12。
对于 40% 的数据,1 ≤ N, M ≤ 100。
对于 60% 的数据,1 ≤ N, M ≤ 10000。
对于 100% 的数据,1 ≤ N, M ≤ 1000000。

试题 J: 逃跑

时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分

【问题描述】
  小明所在星系有 n 颗星球,编号为 1 到 n。这些星球通过 n − 1 条无向边连成一棵树。根结点为编号为 1 的星球。
  为了在星际战争到来时逃到其他星系,小明在根结点设置了逃离用的传送门。每个星球的人只需要一直往父结点星球移动就可以抵达根结点。为了方便各个星球的人去往根结点,小明将其中 m 个星球设置为了跳板星球。在从某个星球去往根结点的路径上,当一个人经过任意星球(包括起点星球)时,他可以尝试直接跳跃到 其前往根结点路径上的除当前星球以外的第一个跳板星球,其时间花费和走到父结点星球的时间花费相同,都是 1 单位时间。
  然而,因为技术问题,向跳板星球的跳跃并不一定成功,每一次跳跃都有p 的概率失败,并转而跳跃到当前星球的父结点星球(相当于直接走到父结点星球);同时此跳板星球失效,将 不再视为跳板星球。
  为了衡量移动效率,小明想知道,如果一个人在这 n 颗星球中随机选择一颗出发前往根结点,其花费的最短时间的期望是多少单位时间?

【输入格式】
输入共 n + 1 行,第一行为两个正整数 n、m 和一个浮点数 p。
后面 n − 1 行,每行两个正整数 xi
, y

【样例说明】
从 1 号星球出发的时间花费为 0;
从 2 号星球出发的时间花费为 1;
从 3 号星球出发的时间花费为 2;
从 4 号星球出发的时间花费为 0.8 × 2 + 0.2 × 3 = 2.2。
所以期望时间为 (0+1+2+2.2)/4 = 1.3。

【评测用例规模与约定】
对于 30% 的数据,保证 1 ≤ n ≤ 2000。
对于 100% 的数据,保证 1 ≤ n ≤ 10 1.网友年龄某君新认识一网友。当问及年龄时,他的网友说:“我的年龄是个2位数,我比儿子大27岁,如果把我的年龄的两位数字交换位置,刚好就是我儿子的年龄”请你计算:网友的年龄一共有多少种可能情况?提示:30岁就是其中一种可能哦.请填写表示可能情况的种数。注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。答案:7代码:#include &lt;iostream&gt; using na... 这个炉子有一个称作转换率的属性V,V 是一个正整数,这意味着消耗V 个普通金属O 恰好可以冶炼出一个特殊金属X,当普通金属O 的数目不足V 时,无法继续冶炼。所以在地图周围一圈, 我们增加一圈0作为外海, dfs遍历外海每一个方格, 若与外海方格相邻的岛屿未被遍历过,那么这就是一个新的岛屿, 再用一个dfs去遍历这个岛。地图外的方格我们全部视为海, 与地图外的海连通的海都视为外海, 可以发现, 接触到了外海的岛屿, 就一定不是其它岛屿的子岛。对于每一条记录, 都可以求出转换率V的一个取值范围。 先用边缘的海水进行bfs求出"真的海水",然后再对所有岛屿进行BFS,此时BFS的时候非真的海水可以直接识别为陆地然后入队。每次用第i个数开头的字母对应的数更新f[i],然后再用f[i]反过来更新bac即可。因此我们可以开一个数f[i]表示前i个数字,以第i个结尾的最大接龙子序列。删最少数使得他是接龙序列,即求原序列的最大接龙子序列,然后用n减去。然后枚举开头的位置,二分出结尾在另一个数的合法位置,直接累加答案。因此直接二分即可,顺便输出一下结果对应的函数值。set维护每个位置的值和要删的最小值。 思路:一开始我想错了,把子岛也求了。第一遍把岛屿外面的0全改成2(8方搜索),然后把里面的0改成1,第二for循环搜索遍历岛屿(四方搜索),看看有几个。假设一下,岛屿外是海,岛屿内是河。图中是有两个岛的,图1的3点是海,2点也是,2点8方可以搜到3,他们也算是联通的;思路:找到所有头l和尾r出现的位置,然后存起来。利用r[j] >= l[i] + k - 1这一关系,找出每一个头的总数,加和即可。思路:这题我写了几个数,发现就是取一堆数据的最小值的最大值,和最大值的最小值(人话:公共部分)。 第一次打蓝桥,寄了,300元买了两个脆脆鲨和一瓶水哈哈可以在下面民间网站提交测试教训 :(1)直接输出骗样例的别忘了先把题目的输入写上(2)读清楚题目要求 (3)一定要return 0;啊啊啊啊(4)devc++的输入窗口可以从属性哪里改成可复制(傻傻的自己敲案例)(5)把思路想好了再动手,(别白白浪费时间) 小蓝在黑板上连续写下从1到2023S⋯20222023小蓝想知道S中有多少种子序列恰好等于2023?提示,以下是312345678910111213⋯1234567891011123⋯1234567891020212223⋯注意以下是不满足条件的子序列,虽然包含了20231220212223⋯。