Misaka-xxw的博客

昨日种种,皆成今我

算了,寒假里做几道放几道呗

P8740 [蓝桥杯 2021 省 A] 填空问题

题目描述

试题 A:卡片

【问题描述】

小蓝有很多数字卡片,每张卡片上都是数字

小蓝准备用这些卡片来拼一些数,他想从 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。

小蓝想知道自己能从 拼到多少。

例如,当小蓝有 张卡片,其中 张,则小蓝可以拼出 ,但是拼 时卡片 已经只有一张了,不够拼出

现在小蓝手里有 的卡片各 张,共 张,请问小蓝可以从 拼到多少?

提示:建议使用计算机编程解决问题。

【答案提交】

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

试题 B:直线

【问题描述】

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。

给定平面上 个整点 ,即横坐标是 (包含 ) 之间的整数、纵坐标是 (包含 ) 之间的整数的点。这些点一共确定了 条不同的直线。

给定平面上 个整点 ,即横坐标是 (包含 ) 之间的整数、纵坐标是 (包含 ) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

【答案提交】

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

试题 C :货物摆放

【问题描述】

小蓝有一个超大的仓库,可以摆放很多货物。

现在,小蓝有 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、 宽、高。

小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆 的货物,满足

给定 ,请问有多少种堆放货物的方案满足要求。

例如,当 时,有以下 种方案:

请问,当 (注意有 位数字) 时,总共有多少种方案?

提示:建议使用计算机编程解决问题。

【答案提交】

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

试题 D:路径

【问题描述】

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。

小蓝的图由 个结点组成,依次编号

对于两个不同的结点 ,如果 的差的绝对值大于 ,则两个结点之间没有边相连; 如果 的差的绝对值小于等于 ,则两个点之间有一条长度为 的最小公倍数的无向边相连。

例如:结点 和结点 之间没有边相连; 结点 和结点 之间有一条无向边,长度为 ; 结点 和结点 之间有一条无向边,长度为

请计算,结点 和结点 之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题。

【答案提交】

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

试题 E:回路计数

【问题描述】

蓝桥学院由 栋教学楼组成,教学楼编号 。对于两栋教学楼 ,当 互质时, 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。

小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案? 两个访问方案不同是指存在某个 ,小蓝在两个访问方法中访问完教学楼 后访问了不同的教学楼。

提示:建议使用计算机编程解决问题。

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
using ll = long long;
using ull = unsigned long long;
//==================== 1 ====================
void solve1()
{
int a[10];
for (int i = 0; i <= 9; i++)
a[i] = 2021;
for (int i = 1;; i++)
{
for (int j = i; j; j /= 10)
{
if (a[j % 10] == 0)
{
cout << i - 1 << endl;
return;
}
--a[j % 10];
}
}
}
//==================== 2 ====================
int gcd(int x, int y)
{
return x % y == 0 ? y : gcd(y, x % y);
}
void solve2()
{
int n = 20, m = 21;
ull ans = 0ll; // row,col
for (int a = 1; a < n; a++)
{
for (int b = 1; b < m; b++)
{
if (gcd(a, b) != 1)
continue;
ans += (n - a) * (m - b);
if (a * 2 <= n && b * 2 <= n)
{
ans -= (n - 2 * a) * (m - 2 * b);
}
}
}
ans *= 2;
ans += (n + m);
cout << ans << endl;
}
//==================== 3 ====================
typedef pair<ull, ull> pp;
ull ans3 = 0;
map<pp, bool> mp;
vector<ull> yz; // 因子
void dfs3(pp p, int d)
{
if (d == yz.size())
{
if (!mp[p])
{
mp[p] = true;
ans3++;
}
return;
}
dfs3(pp(p.first, p.second), d + 1);
dfs3(pp(p.first * yz[d], p.second), d + 1);
dfs3(pp(p.first, p.second * yz[d]), d + 1);
}
void solve3()
{
ull n = 2021041820210418;
for (int i = 2; i <= n;)
{
if (n % i == 0)
{
// cout<<i<<',';
yz.push_back(i);
n /= i;
}
else
{
i++;
}
}
// 2,3,3,3,17,131,2857,5882353
dfs3(pp(1, 1), 0);
cout << ans3 << endl;
}
//==================== 4 ====================
typedef pair<int, int> pint;
void solve4()
{
int n = 2021;
vector<vector<pint>> g(2022);
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= i + 21 && j <= n; j++)
{
int w = i * j / gcd(i, j);
g[i].push_back(pint(w, j));
g[j].push_back(pint(w, i));
}
}
// dijsktra
const int inf = INT_MAX;
vector<int> dis(n + 1, inf);
vector<bool> vis(n + 1, false);
dis[1] = 0;
priority_queue<pint, vector<pint>, greater<>> pq;
pq.push(pint(0, 1));
while (!pq.empty())
{
int u = pq.top().second;
pq.pop();
if (!vis[u])
{
vis[u] = true;
for (auto [w, v] : g[u])
{
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if (!vis[v])
pq.push(pint(dis[v], v));
}
}
}
}
cout << dis[2021] << endl;
}
//==================== 5 ====================
ull ans5;
int ok;
bool have[22][22];
ull f[1 << 21][22]; // 走的序列,到下一个点
void solve5()
{
for (int i = 1; i < 21; i++)
{
for (int j = i + 1; j <= 21; j++)
{
if (gcd(i, j) == 1)
{
have[i][j] = have[j][i] = true;
}
}
}
ok =(1 << 21)-1;// 2,097,151
f[1][1]=1;
for (int c = 1; c <= ok; c++)
{
for (int i = 1; i <= 21; i++)
{
if (c & (1 << (i - 1)))
for (int j = 1; j <= 21; j++)
{
if (!(c & (1 << (j - 1))) && have[i][j])
f[c | (1 << (j - 1))][j] += f[c][i];
}
}
}
for (int i = 1; i <= 21; i++)
{
ans5 += f[ok][i];
}
cout << ans5 << endl;
}
//==================== answer ====================
signed main()
{
string ans[] = {
"3181", // 双引号中替换为 A 题的答案
"40257", // 双引号中替换为 B 题的答案
"2430", // 双引号中替换为 C 题的答案
"10266837", // 双引号中替换为 D 题的答案
"881012367360", // 双引号中替换为 E 题的答案
};
char T;
cin >> T;
cout << ans[T - 'A'] << endl;
return 0;
}

P8742 [蓝桥杯 2021 省 AB] 砝码称重

题目描述

你有一架天平和 个砝码, 这 个砝码重量依次是 。 请你计算一共可以称出多少种不同的重量?

注意砝码可以放在天平两边。

输入格式

输入的第一行包含一个整数

第二行包含 个整数:

输出格式

输出一个整数代表答案。

输入输出样例 #1

输入 #1

1
2
3
1 4 6

输出 #1

1
10

说明/提示

【样例说明】

能称出的 10 种重量是:

【评测用例规模与约定】

对于 的评测用例,

对于所有评测用例, 个砝码总重不超过

蓝桥杯 2021 第一轮省赛 A 组 F 题(B 组 G 题)。

解法

01背包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int f[101][N];
signed main()
{
int n, w, sum = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &w);
sum += w;
for (int j = sum; j; j--)
if (f[i - 1][j])
f[i][j] = f[i][abs(j - w)] = f[i][j + w] = 1;
f[i][w] = 1;
}
int ans = 0;
for (int j = 1; j <= sum; j++)
ans += f[n][j];
printf("%d\n", ans);
return 0;
}

P8743 [蓝桥杯 2021 省 A] 异或数列

题目描述

Alice 和 Bob 正在玩一个异或数列的游戏。初始时,Alice 和 Bob 分别有一个整数 , 有一个给定的长度为 的公共数列

Alice 和 Bob 轮流操作,Alice 先手,每步可以在以下两种选项中选一种:

选项 1: 从数列中选一个 给 Alice 的数异或上, 或者说令 变为 。(其中 表示按位异或)

选项 2: 从数列中选一个 给 Bob 的数异或上,或者说令 变为

每个数 都只能用一次, 当所有 均被使用后( 轮后)游戏结束。游戏结束时, 拥有的数比较大的一方获胜,如果双方数值相同,即为平手。

现在双方都足够聪明,都采用最优策略,请问谁能获胜?

输入格式

每个评测用例包含多组询问。询问之间彼此独立。

输入的第一行包含一个整数 ,表示询问数。

接下来 行每行包含一组询问。其中第 行的第一个整数 表示数列长度,随后 个整数 表示数列中的每个数。

输出格式

输出 行,依次对应每组询问的答案。

每行包含一个整数 分别表示 Alice 胜、平局或败。

输入输出样例 #1

输入 #1

1
2
3
4
5
4
1 1
1 0
2 2 1
7 992438 1006399 781139 985280 4729 872779 563580

输出 #1

1
2
3
4
1
0
1
1

说明/提示

对于所有评测用例,

蓝桥杯 2021 第一轮省赛 A 组 G 题。

解法

转换成二进制,对于某一位,如果它是1,则反转这一位答案。0对答案无影响。奇数个1异或得1,偶数个1异或得0

  • 当1有偶数个时,偶数=偶数+偶数,这一位平手。
  • 当1有奇数个时,奇数=奇数+偶数,两人都想办法获得奇数个1。
    • 只有一个1,Alice直接选,Alice win
    • 当0有偶数个,两人相抵,Alice win
    • 当0有奇数个,Bob选一个0,反转局面,Bob win

例:1 1 1 0 0 0
Alice:1
Bob:0(反转先手)
Alice:0
Bob:0
Alice:1(被迫)
Bob:Alice 1
Bob win

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
signed main()
{
int t, n, x, cnt[22];
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++)
{
scanf("%d", &x);
for (int b = 0; x; b++)
{
if (x & 1)
cnt[b]++;
x >>= 1;
}
}
int win = 0;
for (int i = 20; i >= 0; i--)
{
if (cnt[i] & 1)
{
if (cnt[i] == 1)
win = 1;
else
win = n & 1 ? 1 : -1; // 偶数个0Alice win
break;
}
}
printf("%d\n", win);
}
return 0;
}
/*

*/

P8744 [蓝桥杯 2021 省 A] 左孩子右兄弟

题目描述

对于一棵多叉树,我们可以通过“左孩子右兄弟”表示法,将其转化成一棵二叉树。

如果我们认为每个结点的子结点是无序的,那么得到的二叉树可能不唯一。换句话说,每个结点可以选任意子结点作为左孩子,并按任意顺序连接右兄弟。

给定一棵包含 个结点的多叉树,结点从 编号,其中 号结点是根,每个结点的父结点的编号比自己的编号小。请你计算其通过”左孩子右兄弟”表示法转化成的二叉树,高度最高是多少。(只有根结点这一个结点的树高度为

例如如下的多叉树:

可能有以下 种 (这里只列出 种, 并不是全部) 不同的 “左孩子右兄弟” 表示:

其中最后一种高度最高, 为

输入格式

输入的第一行包含一个整数

以下 行, 每行包含一个整数, 依次表示 号结点的父结点编号。

输出格式

输出一个整数表示答案。

输入输出样例 #1

输入 #1

1
2
3
4
5
5
1
1
1
2

输出 #1

1
4

说明/提示

对于 的评测用例,;

对于所有评测用例,

蓝桥杯 2021 第一轮省赛 A 组 H 题。

解法

dfs
最大答案=孩子数+孩子中的最大答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n;
vector<vector<int>> child(N);
int dfs(int u)
{
int ret = 0;
for (int v : child[u])
ret = max(ret, dfs(v));
return ret + child[u].size();
}
signed main()
{
scanf("%d", &n);
int r;
for (int i = 2; i <= n; i++)
{
scanf("%d", &r);
child[r].push_back(i);
}
printf("%d", dfs(1));
return 0;
}


P8745 [蓝桥杯 2021 省 AB] 括号序列

题目描述

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。

例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()()(())(())()(()())((()))

输入格式

输入一行包含一个字符串 ,表示给定的括号序列,序列中只有左括号和右括号。

输出格式

输出一个整数表示答案,答案可能很大,请输出答案除以 (即 )的余数。

输入输出样例 #1

输入 #1

1
((()

输出 #1

1
5

说明/提示

对于 的评测用例,

对于所有评测用例,

蓝桥杯 2021 第一轮省赛 A 组 I 题(B 组 J 题)。

解法

python生成代码

1
2
3
4
5
6
7
8
a=['(',')']
from random import randint
def f():
l=randint(5,12)
for i in range(l):
print(a[randint(0,1)],end='')
print()
f()

嗯。。。后面没有做了(逃)

一雪2024的前耻,今年报销了300大洋报名费!慢慢复盘。

考时黑客不会优化,最后一题只写完了拖布,拓步,托……反正就是那个什么什么排序啦!目前黑客已经优化,最后三题还没复盘。

洛谷评测

注意事项

  • 这里的题解偷懒了,实际上考试时不要用ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);,直接用scanfprintf最保险
  • 注意数据范围,注意用long long或者unsigned long long的情况
  • 一定记得return 0
  • 注意devcpp开栈和c++版本
  • 某些特殊的填空题可以用python写,改天新开一篇博文写
  • 写一题交一题
  • 拿不要死磕一道题,尽可能每道题都暴力骗到分。实在不行直接输出样例。

P12138 [蓝桥杯 2025 省 A] 寻找质数

题目背景

本站蓝桥杯 2025 省赛测试数据均为洛谷自造,与官方数据可能存在差异,仅供学习参考。

题目描述

如果一个正整数只能被 和它本身两个数整除,就称为一个质数。最小的几个质数依次是

请问,第 个质数是多少?

输入格式

输出格式

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

解法

get 5/5分
暴力就好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int cnt = 1, n = 1;
while (cnt < 2025)
{
n += 2;
bool prime = true;
for (int i = 3; i < n; i += 2)
{
if (n % i == 0)
{
prime = false;
break;
}
}
if (prime)
{
++cnt;
}
}
cout << n << endl;

P12139 [蓝桥杯 2025 省 A] 黑白棋

题目描述

小蓝最近迷上了一款名为“黑白棋填充”的游戏。该游戏在一个方形网格棋盘上进行,其中部分格子已经填有黑色或白色的棋子,而其他格子为空,等待玩家填入棋子。

游戏规则是,玩家需要按照以下规则填满整个棋盘,才能算作胜利:

  1. 黑白棋子数量均等
    在每一行和每一列中,黑色棋子和白色棋子的数量必须相等。
  2. 相邻棋子限制
    在棋盘的任何一行或一列中,不能有超过两个相同颜色的棋子连续排列(即不允许出现“黑黑黑”或“白白白”的情况)。
  3. 行列唯一性
    每一行的棋子排列方式必须是唯一的,不能与棋盘中的任何其他行完全相同。
    每一列的棋子排列方式必须是唯一的,不能与棋盘中的任何其他列完全相同。
    行与列之间的棋子排列不作比较,即行可以与列相同,无需满足行列间的唯一性。

现在有一个 的棋盘,如上图所示,其中部分格子已填入棋子(黑色或白色),其余格子需要你填充,题目保证有唯一解。

请给出唯一的正确解,并按照以下格式输出答案:

  • 黑色棋子用 表示,白色棋子用 表示。
  • 从左到右、从上到下的顺序,依次遍历棋盘上的所有格子,并将这些值拼接成一个长度为 的字符串。

例如,假设最终填充完成后的棋盘如下(仅为示例,并非真实答案):

1
2
3
4
5
6
1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 1 1 0 0
0 0 1 1 1 1

则输出结果应为:100000000000000000001000001100001111

输入格式

输出格式

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

解法

get 5/5分
考试时第一反应是打excel表格,好吧。其实dfs稍微剪枝一下就可以。狠狠暴力!忘情地暴力!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
int board[][6] = {
{1, 0, 2, 0, 2, 2},
{2, 2, 2, 0, 2, 2},
{2, 2, 2, 2, 0, 0},
{2, 2, 2, 2, 2, 2},
{2, 2, 1, 2, 2, 1},
{2, 0, 2, 2, 1, 2}};
// 颜色,对应剩余棋子数
int row[2][6] = {{3, 3, 3, 3, 3, 3}, {3, 3, 3, 3, 3, 3}},
col[2][6] = {{3, 3, 3, 3, 3, 3}, {3, 3, 3, 3, 3, 3}};
void dfs(int r, int c)
{
if (r == 5) // 列尾
{
//每一列的棋子排列方式必须是唯一的,不能与棋盘中的任何其他列完全相同。
for (int j = 0; j < c - 1; ++j)
{
bool ok = false;
for (int i = 0; i < 6; ++i)
{
if (board[i][j] != board[i][c - 1])
{
ok = true;
break;
}
}
if (!ok)
return;
}
}
if (c == 6) // 行尾
{
// 每一行的棋子排列方式必须是唯一的,不能与棋盘中的任何其他行完全相同。
for (int i = 0; i < r; ++i)
{
bool ok = false;
for (int j = 0; j < 6; ++j)
{
if (board[i][j] != board[r][j])
{
ok = true;
break;
}
}
if (!ok)
return;
}
++r;
c = 0;
}
if (r == 6) // 列尾
{
for (int i = 0; i < 6; ++i)
{
for (int j = 0; j < 6; ++j)
{
cout << board[i][j];
}
// cout << endl;
}
exit(0);
}
// 该位置为空
if (board[r][c] == 2)
{
for (int color = 0; color < 2; color++)
{
// 每一行和每一列中,黑色棋子和白色棋子的数量必须相等
if (row[color][r] <= 0 || col[color][c] <= 0)
continue;
// 在棋盘的任何一行或一列中,不能有超过两个相同颜色的棋子连续排列(即不允许出现“黑黑黑”或“白白白”的情况)。
if (r >= 2 && color == board[r - 1][c] && color == board[r - 2][c])
continue;
if (c >= 2 && color == board[r][c - 1] && color == board[r][c - 2])
continue;
--row[color][r];
--col[color][c];
board[r][c] = color;
dfs(r, c + 1);
++row[color][r];
++col[color][c];
}
board[r][c] = 2;
}
else // 该位置已经有棋子
{
int color = board[r][c];
// 每一行和每一列中,黑色棋子和白色棋子的数量必须相等
if (row[color][r] <= 0 || col[color][c] <= 0)
return;
// 在棋盘的任何一行或一列中,不能有超过两个相同颜色的棋子连续排列(即不允许出现“黑黑黑”或“白白白”的情况)。
if (r >= 2 && color == board[r - 1][c] && color == board[r - 2][c])
return;
if (c >= 2 && color == board[r][c - 1] && color == board[r][c - 2])
return;
--row[color][r];
--col[color][c];
dfs(r, c + 1);
++row[color][r];
++col[color][c];
}
}
signed main()
{
dfs(0, 0);//101001010011101100010110011001100110
return 0;
}

P12140 [蓝桥杯 2025 省 A] 抽奖

题目背景

本站蓝桥杯 2025 省赛测试数据均为洛谷自造,与官方数据可能存在差异,仅供学习参考。

题目描述

LQ 商场为了回馈广大用户,为在此消费的用户提供了抽奖机会:抽奖机有三个转轮,每个转轮上都分布有 个数字图案,标号为 ,按照从 顺序转动,当转到第 个图案时会从第一个继续开始。奖项如下:

  1. 三个相同的图案,积分
  2. 两个相同的图案,积分
  3. 三个数字图案,从左到右连续(例如 ),积分
  4. 三个数字图案,经过顺序调整后连续(例如 ),积分

抽奖机处于初始状态,三个转轮都处于第一个位置。每次开始抽奖,都会产生三个对应的随机数 ,表示第 个转轮会向后转动 次停下。下次抽奖时,转轮会从上一次转动后的位置开始继续转动。

注意,一次抽奖最多只能获得一次积分,如果同时命中多个奖项,以积分最大的那个奖项为准。

请问,如果执行 次抽奖,总积分值是多少?

输入格式

输入的第一行包含一个正整数 ,表示转轮大小。

第二行包含 个正整数 ,依次表示第一个转轮上的数字图案,相邻整数之间使用一个空格分隔。

第三行包含 个正整数 ,依次表示第二个转轮上的数字图案,相邻整数之间使用一个空格分隔。

第四行包含 个正整数 ,依次表示第三个转轮上的数字图案,相邻整数之间使用一个空格分隔。

第五行包含一个整数 ,表示抽奖次数。

接下来 行,每行包含三个正整数 ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案,即 次抽奖累计获得的积分的值。

输入输出样例 #1

输入 #1

1
2
3
4
5
6
7
8
4
3 2 4 1
2 2 2 2
4 3 0 9
3
4 4 4
3 1 1
40 39 2

输出 #1

1
300

说明/提示

样例说明

  • 第一次抽奖:三个转轮都转动 次,到达位置 ,数字图案为 ,积分
  • 第二次抽奖:数字图案为 ,积分
  • 第三次抽奖:数字图案为 ,积分不增加。

评测用例规模与约定

  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于所有评测用例,

解法

大模拟,暴力。注意转盘用%运算模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
using namespace std;
#define N 1005

int n, m, d, res = 0;
int arr[3][N];
int pos[3], jiang[3];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);

cin >> n;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < n; ++j)
cin >> arr[i][j];
cin >> m;
while (m--)
{
for (int i = 0; i < 3; ++i)
{
cin >> d;
pos[i] = (pos[i] + d) % n;
jiang[i] = arr[i][pos[i]];
}
if (jiang[0] == jiang[1])
{
if (jiang[0] == jiang[2])
res += 200;
else
res += 100;
}
else if (jiang[0] == jiang[2] || jiang[1] == jiang[2])
{
res += 100;
}
else if (jiang[0] + 1 == jiang[1] && jiang[0] + 2 == jiang[2])
{
res += 200;
}
else
{
sort(jiang, jiang + 3);
if (jiang[0] + 1 == jiang[1] && jiang[0] + 2 == jiang[2])
{
res += 100;
}
}
}
cout << res << endl;
return 0;
}

P12141 [蓝桥杯 2025 省 A] 红黑树

题目描述

小蓝最近学习了红黑树,红黑树是一种特殊的二叉树,树上的结点有两种类型:红色结点和黑色结点。

小蓝在脑海中构造出一棵红黑树,构造方式如下:

  1. 根结点是一个红色结点;
  2. 如果当前结点 是红色结点,那么左子结点 是红色结点,右子结点 是黑色结点;
  3. 如果当前结点 是黑色结点,那么左子结点 是黑色结点,右子结点 是红色结点;

此二叉树前几层的形态如下图所示:

小蓝会从树上随机挑选结点,请你帮忙判断他选出的是红色结点还是黑色结点。

输入格式

输入的第一行包含一个正整数 ,表示小蓝挑选的结点数。

接下来 行,每行包含两个正整数 ,用一个空格分隔,表示小蓝挑选的结点是第 行(从上往下数)第 个(从左往右数)结点。

输出格式

输出 行,每行包含一个字符串,依次表示小蓝每次挑选的结点的答案。RED 表示红色结点,BLACK 表示黑色结点。

输入输出样例 #1

输入 #1

1
2
3
2
1 1
2 2

输出 #1

1
2
RED
BLACK

说明/提示

样例说明

  • 第一行第一个结点为根结点,红色;
  • 第二行第二个结点为黑色结点。

评测用例规模与约定

  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于所有评测用例,

解法

get 10/10
使用瞪眼法,一眼顶针,红黑树看出是异或运算,多往下画两行就发现,颜色和行数无关。把红和黑分别看作0和1,颜色就是异或1的运算。
红黑树示意图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int n, m, k;
cin >> m;
while (m--)
{
cin >> n >> k;
int a = 0;
--k;
while (k)
{
a ^= (k & 1);
k >>= 1;
}
if (a)
cout << "BLACK" << endl;
else
cout << "RED" << endl;
}

P12142 [蓝桥杯 2025 省 A] 黑客

题目描述

小蓝正在两台电脑之间拷贝数据,数据是一个 大小的正整数矩阵,因此总共有 个由空格分开的整数,其中前两个整数分别为 。然而,有黑客入侵了小蓝的电脑,导致这 个正整数的顺序被打乱了。小蓝想知道最多可能有多少个不同的原矩阵。

两个矩阵相同当且仅当它们行数相同、列数相同,且每个位置上的数相同。

输入格式

输入的第一行包含一个正整数

第二行包含 个正整数 ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。答案可能很大,请输出答案除以 的余数。

输入输出样例 #1

输入 #1

1
2
6
2 2 1 4 3 3

输出 #1

1
24

说明/提示

样例说明

可能的原矩阵情况包括:

  1. :有 种原矩阵:
  2. :有 种原矩阵;
  3. :有 种原矩阵;

总计 种。

评测用例规模与约定

  • 对于 的评测用例,
  • 对于所有评测用例,

解法

思路

不用管是不是二维矩阵,直接看作一维数矩阵。题目大意:当找到两个数 ,当 时,求剩余 个数有多少种不重复的排列,并对其求和。

考试时不知道带模的除法怎么做,只好开了 unsigned long long 并且打了个 22 以内的阶乘表,大概是拿 40% 的分了。

实际上,后面补习数论,模意义下的除法是这样的:

其中 是求 模意义下的逆元。这里数据量不大,可以套快速幂公式。

而这里的排列公式为:当有 个不相同的数 排列,对于每一个数 ,它的数量是 ,排列方式有 种。

豪!基础复习完毕。回到题目——

对于行数 和列数 ,当 时,求剩余 个数的排列方式数目。排列公式分母里要去掉一个 和一个 ,当两者相同时,就相当于去掉两个

对于一对“行数”和“列数”,上述排列公式的分母如下:

将分母带入排列公式:

最终答案表达式,即求和:

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define N 500005
const int p = 1000000007;

int n, m;
unordered_map<ll, int> mp;//对每个数,对应的数量。unordered_map比map更高效
ll jc[N]; // 阶乘
ll inverse(ll a)//快速幂逆元
{
ll b = p - 2, ret = 1;
while (b)
{
if (b & 1)
ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
signed main()
{
jc[0] = jc[1] = 1;
for (int i = 2; i < N; ++i)
{
jc[i] = jc[i - 1] * i % p;
}
scanf("%d", &n);
m = n - 2;//矩阵的大小
ll a, b = 1, rc = 0;
for (int i = 0; i < n; ++i)
{
scanf("%lld", &a);
++mp[a];
}
for (auto i : mp)
{
b = b * jc[i.second] % p;
}
for (auto r : mp)
{
if (m % r.first != 0)
continue;
auto c = mp.find(m / r.first);
if (c == mp.end()) // 找不到两数相乘为n-2
continue;
if (r.first != c->first)
rc = (rc + r.second * c->second % p) % p;
else
rc = (rc + r.second * (r.second - 1) % p) % p;
}
cout << jc[n - 2] * inverse(b) % p * rc % p << endl;
return 0;
}

P12143 [蓝桥杯 2025 省 A] 好串的数目

题目描述

对于一个长度为 的字符串 来说,子串的定义是从中选出两个下标 ,这之间所有的字符组合起来的一个新的字符串: 就是其中一个子串。

现在给出一个只有数字字符 组成的数字字符串,小蓝想要知道在其所有的子串中,有多少个子串是好串。一个子串是好串,当且仅当它满足以下两个条件之一:

  1. 单字符子串一定是好串,即当子串长度为 时,它总是好串;
  2. 长度大于 时,可以拆分为两个连续非递减子串
    一个串 连续非递减子串是指,对于所有 ,满足 。即数字串中的每一个数字,要么等于上一个数字,要么等于上一个数字加 。例如 12233456 是连续非递减子串。

输入格式

输入一行包含一个字符串

输出格式

输出一行包含一个整数表示答案,即好串的数目。

输入输出样例 #1

输入 #1

1
12258

输出 #1

1
12

输入输出样例 #2

输入 #2

1
97856

输出 #2

1
13

说明/提示

样例说明 1

  • 长度为 的好串:12258,共 个;
  • 长度为 的好串:12222558,共 个;
  • 长度为 的好串:122225,共 个;
  • 长度为 的好串:1225,共 个;

总计 个。

样例说明 2

  • 长度为 的好串:97856,共 个;
  • 长度为 的好串:97788556,共 个;
  • 长度为 的好串:978785856,共 个;
  • 长度为 的好串:7856,共 个;

总计 个。

评测用例规模与约定

  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于 的评测用例,
  • 对于所有评测用例, 中只包含数字字符

解法

找规律

P12144 [蓝桥杯 2025 省 A] 地雷阵

题目描述

小蓝正在平面直角坐标系中的第一象限里玩一个逃生小游戏。在第一象限中埋有 颗地雷,第 颗地雷的坐标为 ,触发范围为以 为圆心,半径为 的圆。一旦小蓝走进了圆内就会触发地雷导致游戏失败。小蓝初始在原点 上,他需要在第一象限内选择一个方向一直往前走,如果能不触发任何地雷即可成功通关游戏。他想知道在 中均匀随机选择一个方向,即在 (朝向 轴正方向)至 (朝向 轴正方向)之间随机选择一个方向,通关游戏的概率是多少?

输入格式

输入的第一行包含一个正整数

接下来 行,每行包含三个正整数 ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个实数,四舍五入保留三位小数,表示答案。

输入输出样例 #1

输入 #1

1
2
1
2 2 1

输出 #1

1
0.540

输入输出样例 #2

输入 #2

1
2
3
2
1 3 1
3 1 1

输出 #2

1
0.181

说明/提示

评测用例规模与约定

  • 对于 的评测用例,
  • 对于所有评测用例,

解法

根据圆形切线的性质,每一个圆都可以对应一个角度范围。对角度范围进行排序,就是一个线段覆盖的贪心问题。

碎碎念 注意反三角函数在C++里是atan,还有他是弧度还是角度?π 以及它的各个倍数在C++里也是有现成的值,好像是什么M_PI?我当时直接跑到文件夹里搜索的()而且应该是有弧度角度转换的函数吧

P12145 [蓝桥杯 2025 省 A] 扫地机器人

题目描述

在一个含有 个点 条边的无重边无自环的连通无向图中,有一个扫地机器人在执行清扫作业。其中结点 的标记 :如果为 ,则说明该结点需要进行清扫,扫地机器人在到达这个结点时会顺便进行清扫工作。机器人想知道,如果选定任意结点出发,每条边只能经过一次的话,最多能清扫多少个待清扫结点?

输入格式

输入的第一行包含一个正整数

第二行包含 个整数 ,相邻整数之间使用一个空格分隔。

接下来 行,每行包含两个正整数 ,用一个空格分隔,表示结点 和结点 之间有一条边。

输出格式

输出一行包含一个整数表示答案。

输入输出样例 #1

输入 #1

1
2
3
4
5
6
7
8
9
10
11
9
1 0 1 0 0 1 1 0 1
2 8
2 9
2 5
1 5
1 3
1 4
4 5
4 6
6 7

输出 #1

1
4

说明/提示

样例说明

其中一种可行路线:,清扫结点 (共 个)。

评测用例规模与约定

  • 对于 的评测用例,
  • 对于所有评测用例,

解法

一眼顶针,题目里的图为 个结点和 条边的连通图。当有 个结点和 条边的连通图,这是一颗树,树再加上一条边,就是只有一个环的图。除了一个环,剩下都是树的图,即基环树

题意:一个基环树,每个点的权值为0或1,选择一条链,点不能走重复,权值总和最大。

那么,我们的最佳路线,有可能有三种:

  1. 从叶子结点出发,到某一个祖先,再跑到另一个叶子结点
  2. 从叶子结点出发,走到环绕到另一个分岔路口,再走到另一个叶子结点;
  3. 同样从叶子结点出发,走到环跑一圈;或者根本没有叶子结点,直接跑一圈。
    基环树
    这时候可以从叶子结点开始拓补排序,得到从这个环结点进出环的点权值之和。
    考试的时候就只写完了拓补排序,还没有跑环就到时间了。最后一题也算了。

网上的教程,包括官方的教程不太适用,自己摸索了一遍

下载安装驱动

有两种芯片,看你的小芯片上写的是CP2102还是CH340G👀(两个小按键之间),选择不同的驱动。

CP2102芯片

CP2102驱动下载地址:
https://www.silabs.com/developer-tools/usb-to-uart-bridge-vcp-drivers?tab=downloads
选择CP210x Windows Drivers下载,解压。
图片
然后安装。
笔记本电脑选:CP210xVCPInstaller_x64.exe
台式电脑(诶?)选:CP210xVCPInstaller_x86.exe

CH340G芯片

驱动下载地址:
https://www.arduined.eu/ch340-windows-10-driver-download/
点击蓝字CH340 driver for Windows 10下载,解压。点SETUP.EXE安装。

Tips

如果你没有放大镜,看不清楚的话,你也可以两个都装,因为非常好装。

连接设备

长按Esp板子上的boot按键,同时插上电脑,并抬起按键。
同时按下组合键:“Win + R”,打开“运行”对话框,在其中输入“devmgmt.msc”,点击“确定”即可打开“设备管理器”。点开端口。应该能看到包含芯片名称的一个端口,如图
图片
如果没有的话,首先不要怀疑驱动,建议你先多换几个USB线,我换了三根三根。。。
然后记住是COM几,我的是COM4,下面的步骤记得替换。

下载固件

最新固件网址:
https://micropython.org/download/ESP8266_GENERIC/
直接找到
Firmware
Releases
初学者下载最上方的bin文件即可(带latest标识)。本人下载的时候版本是v1.24.1 (2024-11-29) .bin
图片

下载python

确保已安装Python 3.7或更高版本。虽然官网说, Python 2.7和Python 3.4以上都可以,但是你也不希望被同学知道你还在用老版本的Python吧(不是)。

Python官网:https://www.python.org/

用pip安装esptool

首先你有可能要更新pip。

1
python.exe -m pip install --upgrade pip

接着

1
pip install esptool

找到你Python的Scripts文件夹(里面应该有个esptool.exe),加上记住它的路径。再在后面加上esptool

找到esptool的一种方式
cmd怎么打开 win+R打开运行,输入cmd,回车

在cmd,或者Vscode bash或者Pycharm bash或者powershell或者别的里,输入

1
pip --version

我这里显示了

1
pip 25.0.1 from D:\GitHub\Smart-Aquaculture-esp8266\.venv\lib\site-packages\pip (python 3.10)

看到这个D:\GitHub\Smart-Aquaculture-esp8266\.venv\lib\site-packages\pip,去掉lib\site-packages\pip,换成Scripts\esptool,即D:\GitHub\Smart-Aquaculture-esp8266\.venv\Scripts\esptool。等会儿要用。

</details>

下面的.venv/Scripts/esptool替换成你的路径
因为我建了虚拟环境,就用相对路径了,所以后面就用.venv/Scripts/esptool简化写了。

对于以下两个命令,你需要更改三个地方:

  1. .venv/Scripts/esptool改成你在用pip安装esptool一步找到esptool的路径。
  2. COM4改成在连接设备一步找到的COM几
  3. D:\software\ESP8266_GENERIC-20241129-v1.24.1.bin改成你在下载固件一步下载的固件的路径。

打开cmd,先擦除闪存

1
.venv/Scripts/esptool --port COM4 erase_flash

然后烧录固件
1
.venv/Scripts/esptool --port COM4  --baud 460800 write_flash --flash_size=detect 0  D:\software\ESP8266_GENERIC-20241129-v1.24.1.bin

下载安装ide

推荐thonny。
下载地址:https://thonny.org/
虽然这个软件某不可言说的方面有点抽象,但是是确实好用。也可以选择别的ide。

如图:
图片

图片

Hello world!

成功打开之后,Ctrl+N新建main.py文件,到了喜闻乐见的Hello world环节

1
print("hello world!")

F5运行,看看能不能hello world。

Blink!

图片

参考网址:
https://docs.micropython.org/en/latest/esp8266/tutorial/intro.html

环境安装

CUDA

python

这里我装的是python3.9,因为装python3.7的话cuda似乎没有对应版本。新建一个miniconda环境。

pytorch

查找和cuda相对应的版本号,例如Cuda 12.8

1
pip install --pre torch torchvision  --index-url https://download.pytorch.org/whl/nightly/cu128

1
2
3
python font2img.py --src_font=src.otf --dst_font=trg.otf --charset=CN --sample_count=1000 --sample_dir=dir --label=0 --filter --shuffle --mode=font2font
python package.py --dir=dir --save_dir=experiment/data --split_ratio=0.8
python train.py --experiment_dir=experiment --gpu_ids=cuda:0 --batch_size=32 --epoch=100 --sample_steps=200 --checkpoint_steps=500

单调栈

模板

原题
题意:对于数列,中每一个数,输出这个数后面第一个大于它的下标
思路:栈内从大往小放,当栈顶的数小于当前数时,更新栈顶数的answer并弹出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 5;
using ll = long long;
int n, p = 0;
int a[N], st[N], f[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
for (; p && a[st[p - 1]] < a[i]; --p)
{
f[st[p - 1]] = i;
}
st[p++] = i;
}
for (int i = 1; i <= n; i++)
cout << f[i] << ' ';
return 0;
}

经典题:接雨水

原题

一个单调递减的单调栈,能找到每个柱子左边和右边的第一个比它高的柱子,从而计算出可以接住的雨水量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def trap(self, height: List[int]) -> int:
st=[]
ans=0
for r,rh in enumerate(height):
while st and height[st[-1]]<rh:
bottom=st.pop()
if not st:
break
l=st[-1]
w=r-l-1
h=min(height[l],height[r])-height[bottom]
ans+=w*h
st.append(r)
return ans

滑动窗口/单调队列

模板

原题
题意:滑动窗口的最小值和最大值

思路:
有更好的新员工,立刻淘汰老员工;
老员工过期了,多好也要淘汰;
新员工永远来到队伍的最底端,队列从头到尾,是从又优秀又老的Top员工,到又普通又新的Back员工。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, k, a[N];
deque<int> maxq, minq;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> a[i];
// 区间最小
for (int i = 0; i < k; i++)
{
while (!minq.empty() && minq.front() <= i - k)
minq.pop_front();
while (!minq.empty() && a[minq.back()] >= a[i])
minq.pop_back();
minq.push_back(i);
}
cout << a[minq.front()];
for (int i = k; i < n; i++)
{
while (!minq.empty() && minq.front() <= i - k)
minq.pop_front();
while (!minq.empty() && a[minq.back()] >= a[i])
minq.pop_back();
minq.push_back(i);
cout << ' ' << a[minq.front()];
}
cout << endl;

// 区间最大
for (int i = 0; i < k; i++)
{
while (!maxq.empty() && maxq.front() <= i - k)
maxq.pop_front();
while (!maxq.empty() && a[maxq.back()] <= a[i])
maxq.pop_back();
maxq.push_back(i);
}
cout << a[maxq.front()];
for (int i = k; i < n; i++)
{
while (!maxq.empty() && maxq.front() <= i - k)
maxq.pop_front();
while (!maxq.empty() && a[maxq.back()] <= a[i])
maxq.pop_back();
maxq.push_back(i);
cout << ' ' << a[maxq.front()];
}
cout << endl;
return 0;
}

经典题:无重复字符的最长子串

原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const set=new Set();
let j=0,ans=0,n=s.length;
for(i=0;i<n;i++){
while(j<n&&!set.has(s[j])){
set.add(s[j]);
j++;
}
ans=Math.max(ans,j-i);
set.delete(s[i]);
}
return ans;
};

https://leetcode.cn/problems/find-all-anagrams-in-a-string?envType=study-plan-v2&envId=top-100-liked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Solution {
public IList<int> FindAnagrams(string s, string p) {
var ans=new List<int>();
int sl=s.Length,pl=p.Length;
if(sl<pl){
return ans;
}
int[] cnt=new int[26];
int dif=0;
for(int i=0;i<pl;i++){
++cnt[s[i]-'a'];
--cnt[p[i]-'a'];
}
for(int i=0;i<26;i++){
if(cnt[i]!=0) ++dif;
}
if(dif==0) ans.Add(0);
for(int i=0;i<sl-pl;i++){
int idx=s[i]-'a';
if(cnt[idx]==1) --dif;
else if(cnt[idx]==0) ++dif;
--cnt[idx];

idx=s[i+pl]-'a';
if(cnt[idx]==-1) --dif;
else if(cnt[idx]==0) ++dif;
++cnt[idx];

if(dif==0) ans.Add(i+1);
}
return ans;
}
}

2023的题还需要烧烤一下/(ㄒoㄒ)/~~
所以先做2022的题
暂未完成:最长上升子序列


P8770 [蓝桥杯 2022 省 A] 填空问题

题目描述

试题 A :裁纸刀

【问题描述】

小蓝有一个裁纸刃,每次可以将一张纸沿一条直线裁成两半。

小蓝用一张纸打印出两行三列共 个二维码,至少使用九次裁出来,下图给出了一种裁法。

在上面的例子中,小蓝的打印机没办法打印到边缘,所以边缘至少要裁 次。另外,小蓝每次只能裁一张纸,不能重叠或者拼起来裁。

如果小蓝要用一张纸打印出 列共 个二维码,他至少需要裁多少次?

【答案提交】

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

试题 B:灭鼠先锋

【问题描述】

灭鼠先锋是一个老少咸宜的棋盘小游戏,由两人参与,轮流操作。

灭鼠先锋的棋盘有各种规格,本题中游戏在两行四列的棋盘上进行。游戏的规则为:两人轮流操作,每次可选择在棋盘的一个空位上放置一个棋子,或在同一行的连续两个空位上各放置一个棋子,放下棋子后使棋盘放满的一方输掉游戏。

小蓝和小乔一起玩游戏,小蓝先手,小乔后手。小蓝可以放置棋子的方法很多,通过旋转和翻转可以对应如下四种情况:

1
2
X000 XX00 OXOO OXXO
0000 0000 0000 0000

其中 O 表示棋盘上的一个方格为空,X 表示该方格已经放置了棋子。

请问,对于以上四种情况,如果小蓝和小乔都是按照对自己最优的策略来玩游戏,小蓝是否能获胜。如果获胜,请用 V 表示,否则用 L 表示。请将四种情况的胜负结果按顺序连接在一起提交。

解法

裁纸刀

灭鼠先锋

博弈论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits\stdc++.h>
using namespace std;
char board[5];
bool dfs()
{
if (strcmp(board, "XXXX") == 0)
{
return false;
}
for (int i = 0; i < 4; i++)
{
if (board[i] == 'O')
{
board[i] = 'X';
if (!dfs())
{
board[i] = 'O';
return true;
}
if (board[i + 1] == 'O')
{
board[i + 1] = 'X';
if (!dfs())
{
board[i] = 'O';
board[i + 1] = 'O';
return true;
}
board[i + 1] = 'O';
}
board[i] = 'O';
}
}
}
int main()
{
char con[][5] = {"XOOO", "XXOO", "OXOO", "OXXO"};
for (int i = 0; i < 4; i++)
{
strcpy(board, con[i]);
if (!dfs())//you fail
putchar('V');
else
putchar('L');
}
return 0;
}

[蓝桥杯 2022 省 A] 求和

题目描述

给定 个整数 , 求它们两两相乘再相加的和,即

输入格式

输入的第一行包含一个整数

第二行包含 个整数

输出格式

输出一个整数 ,表示所求的和。请使用合适的数据类型进行运算。

样例 #1

样例输入 #1

1
2
4
1 3 6 9

样例输出 #1

1
117

提示

对于 的数据,

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 C 题。

解法

后缀和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using ll=unsigned long long;
const int N=(2e5)+5;
using namespace std;
int n;
int a[N];
ll sum=0;
ll ans=0;
int main() {
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=n-1;i>=0;i--){
ans+=a[i]*sum;
sum+=a[i];
}
printf("%lld",ans);
return 0;
}


[蓝桥杯 2022 省 A] 选数异或

题目描述

给定一个长度为 的数列 和一个非负整数 , 给定 次查询, 每次询问能否从某个区间 中选择两个数使得他们的异或等于

输入格式

输入的第一行包含三个整数

第二行包含 个整数

接下来 行,每行包含两个整数 表示询问区间

输出格式

对于每个询问, 如果该区间内存在两个数的异或为 则输出 yes, 否则输出 no

样例 #1

样例输入 #1

1
2
3
4
5
6
4 4 1
1 2 3 4
1 4
1 2
2 3
3 3

样例输出 #1

1
2
3
4
yes
no
yes
no

提示

【样例说明】

显然整个数列中只有 2,3 的异或为 1 。

【评测用例规模与约定】

对于 的评测用例, ;

对于 的评测用例, ;

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 D 题。

解法

异或:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
const int N=(1e5)+5;
using namespace std;
int n,m,x;
unordered_map<int,int> mp;//数值,对应位置
int f[N];//对于查询[l,r],f[r]表示[l,r]要有相配答案,l的最右
int main() {
scanf("%d%d%d",&n,&m,&x);
int a;
for(int i=1;i<=n;i++){
scanf("%d",&a);
f[i]=max(f[i-1],mp[a^x]);//找到达到要求的最左边界
mp[a]=i;
}
int l,r;
while(m--){
scanf("%d%d",&l,&r);
if(l<=f[r])
printf("yes\n");
else
printf("no\n");
}
return 0;
}


[蓝桥杯 2022 省 A] 爬树的甲壳虫

题目描述

有一只甲壳虫想要爬上一颗高度为 的树,它一开始位于树根, 高度为 ,当它尝试从高度 爬到高度为 的位置时有 的概率会掉回树根, 求它从树根爬到树顶时, 经过的时间的期望值是多少。

输入格式

输入第一行包含一个整数 表示树的高度。

接下来 行每行包含两个整数 , 用一个空格分隔,表示

输出格式

输出一行包含一个整数表示答案,答案是一个有理数,请输出答案对质数 取模的结果。其中有理数 对质数 取模的结果是整数 满足

样例 #1

样例输入 #1

1
2
1
1 2

样例输出 #1

1
2

样例 #2

样例输入 #2

1
2
3
4
3
1 2
3 5
7 11

样例输出 #2

1
623902744

提示

对于 的评测用例, ;

对于 的评测用例, ;

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 E 题。

bgm🐌🎵

是🐞不是🐌喂

解法

状态转移

设从高度爬到高度的期望时间是.


转移方程:

费马小定理的一种表述:对于任意整数和质数,有

模运算和逆元

在模运算中,使用逆元将除法转换为乘法,避免了直接计算分数。
快速幂模板
乘法逆元模板

逆元的定义:如果存在一个整数 ,使得 ,那么 就是 在模 下的逆元

费马小定理的一种表述:如果是一个质数,且 互质,那么:
另一种表述:对于任意整数和质数,有

计算 在模下的值。根据费马小定理得到:

对于模数,它是一个质数,所以可以直接用 来计算

快速幂函数

函数quick_pow用于计算 。它利用了快速幂算法,时间复杂度为
通过不断将指数右移(相当于除以2),并根据当前指数的奇偶性决定是否将当前的乘到结果中。
将分数转换为模运算下的乘法。

总代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<bits/stdc++.h>
using ll=unsigned long long;
const int p=998244353;//取模
using namespace std;
int n;
ll x,y,f;
ll quick_pow(ll a){
ll res=1;
ll b=p-2;
while(b){
if(b&1){
res=res*a%p;
}
a=a*a%p;
b>>=1;
}
return res;
}
int main() {
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lld%lld",&x,&y);
f=((f+1)*y%p)*quick_pow(y-x)%p;
}
cout<<f<<endl;
return 0;
}

时间复杂度
每次计算逆元的时间复杂度为
总时间复杂度为 ,其中 n 是树的高度。


[蓝桥杯 2022 省 A] 青蛙过河

题目描述

小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。

河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降 ,当石头的高度下降到 时小青蛙不能再跳到这块石头上(某次跳跃后使石头高度下降到 是允许的)。

小青蛙一共需要去学校上 天课,所以它需要往返 次。当小青蛙具有一个跳跃能力 时,它能跳不超过 的距离。

请问小青蛙的跳跃能力至少是多少才能用这些石头上完 次课。

输入格式

输入的第一行包含两个整数 , 分别表示河的宽度和小青蛙需要去学校的天数。请注意 才是实际过河的次数。

第二行包含 个非负整数 , 其中 表示在河中与 小青蛙的家相距 的地方有一块高度为 的石头, 表示这个位置没有石头。

输出格式

输出一行, 包含一个整数, 表示小青蛙需要的最低跳跃能力。

样例 #1

样例输入 #1

1
2
5 1
1 0 1 0

样例输出 #1

1
4

提示

【样例解释】

由于只有两块高度为 的石头,所以往返只能各用一块。第 块石头和对岸的距离为 ,如果小青蛙的跳跃能力为 则无法满足要求。所以小青蛙最少需要 的跳跃能力。

【评测用例规模与约定】

对于 的评测用例,;

对于 的评测用例,;

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 F 题。

bgm🐸🎵

解法

贪心+前缀和
对于每个起点,检查区间 内的石头总高度是否足够支持 次跳跃。
如果某个区间的石头总高度不足,则增加,扩大区间范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>
using namespace std;
using ll=unsigned long long;
const int N=(1e5)+5;
int n,x;//宽度和小青蛙需要去学校的天数。
ll sum[N];//石头总数前缀和
int main() {
scanf("%d%d",&n,&x);
x<<=1;//实际上让青蛙2x次过岸
int h;//石头高度
for(int i=1;i<n;i++)
{
scanf("%d",&h);
sum[i]=sum[i-1]+h;//[l,r]区间的石头总高度为sum[r]-sum[l-1]
}
int y=1;//跳跃能力
sum[n]=INT_MAX;//到对岸相当于有无数的石头
for(int i=0;i<n-y;i++)
while(sum[i+y]-sum[i]<x)//[i+1,i+y]区间不够2x只青蛙落脚
y++;
printf("%d",y);
return 0;
}

[蓝桥杯 2022 省 A] 最长不下降子序列

题目描述

给定一个长度为 的整数序列:。现在你有一次机会,将其中连续的 个数修改成任意一个相同值。请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。

最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。

输入格式

输入第一行包含两个整数

第二行包含 个整数

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

1
2
5 1
1 4 2 8 5

样例输出 #1

1
4

提示

对于 的评测用例, ;

对于 的评测用例, ;

对于 的评测用例, ;

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 G 题。

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
```

---

# [蓝桥杯 2022 省 A] 扫描游戏

## 题目描述

有一根围绕原点 $O$ 顺时针旋转的棒 $OA$,初始时指向正上方(Y 轴正向)。平面中有若干物件,第 $i$ 个物件的坐标为 $\left(x_{i}, y_{i}\right)$,价值为 $z_{i}$。当棒扫到某个物件时,棒的长度会瞬间增长 $z_{i}$,且物件瞬间消失(棒的顶端恰好碰到物件也视为扫到),如果此时增长完的棒又额外碰到了其他物件,也按上述方式消去(它和上述那个点视为同时消失)。

如果将物件按照消失的时间排序,则每个物件有一个排名,同时消失的物件排名相同,请输出每个物件的排名,如果物件永远不会消失则输出 $-1$。

## 输入格式

输入第一行包含两个整数 $n$、$L$,用一个空格分隔,分别表示物件数量和棒的初始长度。

接下来 $n$ 行每行包含第三个整数 $x_{i}, y_{i}, z_{i}$。

注意存在 $(x_{i}, y_{i})=(0,0)$ 的情况,这些点视为一开始就立刻被碰到。

## 输出格式

输出一行包含 $n$ 整数,相邻两个整数间用一个空格分隔,依次表示每个物件的排名。

## 样例 #1

### 样例输入 #1

5 2
0 1 1
0 3 2
4 3 5
6 8 1
-51 -33 2

1
2
3

### 样例输出 #1


1 1 3 4 -1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

## 提示

对于 $30 \%$ 的评测用例,$1 \leq n \leq 500$ ;

对于 $60 \%$ 的评测用例,$1 \leq n \leq 5000$;

对于所有评测用例,$1 \leq n \leq 2\times10^5,-10^{9} \leq x_{i}, y_{i} \leq 10^{9}, 1 \leq L, z_{i} \leq 10^{9}$ 。

样蓝桥杯 2022 省赛 A 组 H 题。

## 题解

为什么不试试优先队列呢?
此处建议用斜率代替角度,角度每个网站的数据要求精度不一样。但我过了洛谷和蓝桥杯官网就懒得改了```(*ꈍ◡ꈍ*)```

```cpp
#include <bits/stdc++.h>
using ll = long long;
using ull = unsigned long long;
const int N = (2e5) + 5;
const double pi2 = 2 * M_PI; // 2*圆周率pi
using namespace std;

struct point {
int id; // 序号
int z; // 碰到增加的值
ull r; // 半径的平方
double angle; // 角度(以从y轴正方向开始的顺时针角度表示)
bool operator>(const point &other) const {
return angle > other.angle; // 优先队列按角度排序
}
} p[N];

int main() {
int n;//棒的个数
ull l;//棒的长度
__int128 l2;//棒的长度的平方,棒的长度的平方。__int128防止超过数据范围
scanf("%d%lld", &n, &l);
l2 = l * l;
ll x, y;
for (int i = 0; i < n; i++) {
scanf("%lld%lld%d", &x, &y, &p[i].z);
p[i].id = i;
p[i].r = x * x + y * y;//半径的平方
p[i].angle = atan2(x, y);//算角度。atan2可以处理为0的情况,注意是顺时针从y轴正方向开始
if (p[i].angle < 0)//把第二、三象限的角度转过来
p[i].angle = pi2 + p[i].angle;
}
sort(p, p + n, [](const point &a, const point &b) -> bool {
return a.r < b.r;
});//按半径排序
priority_queue<point, vector<point>, greater<>> q; // 按角度排序的优先队列
int pos;
for (pos = 0; pos < n && p[pos].r <= l2; pos++) {
q.push(p[pos]);
}

int cnt = 1, last_cnt = 1;//排名,并列排名
int ans[N];//答案
double rounds = pi2, angle = 0; // rounds 表示本轮结束的角度,angle 保存上一个被扫到的点的角度
memset(ans, -1, sizeof(ans));//初始化为-1
const double eps = 1e-11; // 浮点数比较阈值。1e-9官网过不了

while (!q.empty()) {
point u = q.top();
q.pop();
if (u.angle > rounds + eps) // 如果当前点的角度超过本轮结束的角度
rounds += pi2;
if (fabs(u.angle - angle) < eps) { // 判断两角度是否“相等”,用并列排名
ans[u.id] = last_cnt;
} else {
last_cnt=ans[u.id]=cnt;//更新并列的排名
angle=u.angle;//角度
}
++cnt;//更新排名

l += u.z;
l2 = l * l;
while (pos < n && p[pos].r <= l2) {
// 新点的角度转换到绝对角度,先加上当前轮次的偏移量
p[pos].angle += (rounds - pi2);
if (p[pos].angle < angle - eps) {
p[pos].angle += pi2;
}
q.push(p[pos]);
++pos;
}
}
printf("%d", ans[0]);
for (int i = 1; i < n; i++)
printf(" %d", ans[i]);
return 0;
}


[蓝桥杯 2022 省 A] 数的拆分

题目描述

给定 个正整数 ,分别问每个 能否表示为 的形式,其中 为正整数, 为大于等于 的正整数。

输入格式

输入第一行包含一个整数 表示询问次数。

接下来 行,每行包含一个正整数

输出格式

对于每次询问,如果 能够表示为题目描述的形式则输出 yes,否则输出 no

样例 #1

样例输入 #1

1
2
3
4
5
6
7
8
7
2
6
12
4
8
24
72

样例输出 #1

1
2
3
4
5
6
7
no
no
no
yes
yes
no
yes

提示

【样例说明】

个数分别可以表示为:

【评测用例规模与约定】

对于 的评测用例,;

对于 的评测用例,;

对于 的评测用例,;

对于所有评测用例,

蓝桥杯 2022 省赛 A 组 I 题。

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<bits/stdc++.h>
using ll=long long;
const int N=4000;
using namespace std;
int n;
int not_prime[N+4];
vector<int> primes;
inline void init_primes(){
for(int i=2;i<=N;i++){
if(!not_prime[i]){
primes.push_back(i);
}
for(int j:primes){
if(i*j>N)
break;
not_prime[i*j]=true;
if(i%j==0)
break;
}
}
}
inline bool check(ll x){
ll y=pow(x,0.5);
if(y*y==x)
return true;
y=pow(x,1.0/3);
if(y*y*y==x||(y+1)*(y+1)*(y+1)==x)//?
return true;
return false;
}
int main() {
init_primes();
int t;
ll n;
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
if(check(n)){
printf("yes\n");
continue;
}
bool flag=true;
for(int i:primes){
int cnt=0;
while(n%i==0){
n/=i;
cnt++;
}
if(cnt==1){
flag=false;
break;
}
}
if(flag&&check(n)){
printf("yes\n");
}
else{
printf("no\n");
}

}
return 0;
}

[蓝桥杯 2022 省 A] 推导部分和

题目描述

对于一个长度为 的整数数列 ,小蓝想知道下标 的部分和 是多少?

然而,小蓝并不知道数列中每个数的值是多少,他只知道它的 个部分和的值。其中第 个部分和是下标 的部分和 , 值是

输入格式

第一行包含 3 个整数 。分别代表数组长度、已知的部分和数量 和询问的部分和数量。

接下来 行,每行包含 个整数

接下来 行,每行包含 个整数 ,代表一个小蓝想知道的部分和。

输出格式

对于每个询问, 输出一行包含一个整数表示答案。如果答案无法确定, 输出 UNKNOWN

样例 #1

样例输入 #1

1
2
3
4
5
6
7
5 3 3
1 5 15
4 5 9
2 3 5
1 5
1 3
1 2

样例输出 #1

1
2
3
15
6
UNKNOWN

提示

对于 的评测用例,

对于 的评测用例,

对于 的评测用例,

对于 的评测用例,

对于 的评测用例,

对于所有评测用例, , 。数据保证没有矛盾。

蓝桥杯 2022 省赛 A 组 J 题。

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>

using namespace std;
using ll = long long;
vector<int> pa;
vector<ll> sum;

int find(int x) {
if (x != pa[x]) {
int root = find(pa[x]);
sum[x] += sum[pa[x]];
pa[x] = root;
}
return pa[x];
}

int main() {
int n, m, q;
scanf("%d%d%d", &n, &m, &q);

pa.resize(n + 1);
sum.resize(n + 1, 0);
iota(pa.begin(), pa.end(), 0);
int l, r;
ll s;
while (m--) {
scanf("%d%d%lld", &l, &r, &s);
int root_l = find(l - 1);
int root_r = find(r);
if (root_l != root_r) {
pa[root_r] = root_l;
sum[root_r] = s + sum[l - 1] - sum[r];
}
}
while (q--) {
scanf("%d%d", &l, &r);
if (find(l - 1) == find(r)) {
printf("%lld\n", sum[r] - sum[l - 1]);
} else {
printf("UNKNOWN\n");
}
}
return 0;
}

好几道题还没做。
算了,寒假里做几道放几道呗

[蓝桥杯 2023 省 A] 填空问题

题目描述

A. 幸运数

小蓝认为如果一个数含有偶数个数位,并且前面一半的数位之和等于后面一半的数位之和,则这个数是他的幸运数字。例如 是一个幸运数字,因为它有 个数位,并且 。现在请你帮他计算从 之间共有多少个不同的幸运数字。

B. 有奖问答

小蓝正在参与一个现场问答的节目。活动中一共有 道题目,每题只有答对和答错两种情况,每答对一题得 分,答错一题分数归零。

小蓝可以在任意时刻结束答题并获得目前分数对应的奖项,之后不能再答任何题目。最高奖项需要 分,所以到达 分时小蓝会直接停止答题。

已知小蓝最终实际获得了 分对应的奖项,请问小蓝所有可能的答题情况有多少种?

提示

第十四届蓝桥杯大赛软件赛省赛 C/C++ 大学 A 组 A-B

解法

填空题,暴!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
long long ans1,ans2;//4430091,8335366
// 11~99999999
void solve1(){
int cnt[40][5]={0};//和为几(1~36),几位数(1~4)
for(int i=1;i<=9999;i++){
int w=0,s=0,a=i;
while(a){
w++;
s+=a%10;
a/=10;
}
cnt[s][w]++;
}
for(int i=1;i<=36;i++){
for(int j=1;j<=4;j++){
int s=0;
for(int k=1;k<=j;k++){
s+=cnt[i][k];
}
ans1+=cnt[i][j]*s;
}
}
}
//答对,答错或者结束比赛
//300题拿了7分,答对1题加10分,答错分数归零。其中分数不可能>=100
void dfs(int t,int s){//题号,分数
if(s==100)//到达100分时小蓝会直接停止答题
return;
if(s==70){
ans2++;}
if(t==31)//比赛结束
return;
dfs(t+1,s+10);//答对+1
dfs(t+1,0);//答错分数归0
}
void solve2(){
dfs(1,0);
}


[蓝桥杯 2023 省 A] 平方差

题目描述

给定 ,问 中有多少个数 满足存在整数 使得

输入格式

输入一行包含两个整数 ,用一个空格分隔。

输出格式

输出一行包含一个整数满足题目给定条件的 的数量。

样例 #1

样例输入 #1

1
1 5

样例输出 #1

1
4

提示

【样例说明】

【评测用例规模与约定】

对于 的评测用例,

对于所有评测用例,

第十四届蓝桥杯大赛软件赛省赛 C/C++ 大学 A 组 C

解法

$$

1
2
3
4
5
6
7
8
9
#include <bits/stdc++.h>
using namespace std;
signed main()
{
int l, r;
cin >> l >> r;
cout << (r >> 1) + (r & 1) - (l >> 1) + (r >> 2) - (l >> 2) + !(l & 3) << endl;
return 0;
}


[蓝桥杯 2023 省 A] 更小的数

题目描述

image

小蓝有一个长度均为 且仅由数字字符 组成的字符串,下标从 ,你可以将其视作是一个具有 位的十进制数字 ,小蓝可以从 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 满足条件 ,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 中的位置不完全相同我们就视作是不同的方案。

注意,我们允许前导零的存在,即数字的最高位可以是 ,这是合法的。

输入格式

输入一行包含一个长度为 的字符串表示 (仅包含数字字符 ),从左至右下标依次为

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

1
210102

样例输出 #1

1
8

提示

【样例说明】

一共有 种不同的方案:

  1. 所选择的子串下标为 ,反转后的
  2. 所选择的子串下标为 ,反转后的
  3. 所选择的子串下标为 ,反转后的
  4. 所选择的子串下标为 ,反转后的
  5. 所选择的子串下标为 ,反转后的
  6. 所选择的子串下标为 ,反转后的
  7. 所选择的子串下标为 ,反转后的
  8. 所选择的子串下标为 ,反转后的

【评测用例规模与约定】

对于 的评测用例,

对于 的评测用例,

对于所有评测用例,

解法

1
2
3
4
5
6
对于区间[l,r]
if l!=r:
if num[l]>num[r]:
可以翻转
elif [l+1,r-1]in [l,r] and [_l,_r]可以翻转:
可以翻转

动态规划,根据对称性,优化成一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
#define N 5005
char num[N];
int n;
long long res;
bool f[N * 2];
signed main()
{
scanf("%s", num);
n = strlen(num);
for (register int k = 1; k < n; k++)
{
for (register int i = n - k - 1; i >= 0; i--)//正着也一样
{
int j = i + k;
if (num[i] > num[j])//可以翻转
{
res++;
f[i + j] = true;
}
else if (num[i] == num[j])//看区间[i+1,j-1]是否可以翻转
{
if (f[i + j])
res++;
}
else//不可以翻转
{
f[i+j]=false;
}
}
}
printf("%lld\n", res);
return 0;
}


[蓝桥杯 2023 省 A] 颜色平衡树

题目描述

给定一棵树,结点由 编号,其中结点 是树根。树的每个点有一个颜色

如果一棵树中存在的每种颜色的结点个数都相同,则我们称它是一棵颜色平衡树。

求出这棵树中有多少个子树是颜色平衡树。

输入格式

输入的第一行包含一个整数 ,表示树的结点数。

接下来 行,每行包含两个整数 ,用一个空格分隔,表示第 个结点的颜色和父亲结点编号。

特别地,输入数据保证 ,也即 号点没有父亲结点。保证输入数据是一棵树。

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

1
2
3
4
5
6
7
6
2 0
2 1
1 2
3 3
3 4
1 4

样例输出 #1

1
4

提示

【样例说明】

编号为 个结点对应的子树为颜色平衡树。

【评测用例规模与约定】

对于 的评测用例,

对于 的评测用例,

对于所有评测用例,

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
```

# [蓝桥杯 2023 省 A] 买瓜

## 题目描述

小蓝正在一个瓜摊上买瓜。瓜摊上共有 $n$ 个瓜,每个瓜的重量为 $A_i$。小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈一刀。

小蓝希望买到的瓜的重量的和恰好为 $m$。

请问小蓝至少要劈多少个瓜才能买到重量恰好为 $m$ 的瓜。如果无论怎样小蓝都无法得到总重恰好为 $m$ 的瓜,请输出 $-1$。

## 输入格式

输入的第一行包含两个整数 $n,m$,用一个空格分隔,分别表示瓜的个数和小蓝想买到的瓜的总重量。

第二行包含 $n$ 个整数 $A_i$,相邻整数之间使用一个空格分隔,分别表示每个瓜的重量。

## 输出格式

输出一行包含一个整数表示答案。

## 样例 #1

### 样例输入 #1

3 10
1 3 13

1
2
3

### 样例输出 #1


2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

## 提示

#### 【评测用例规模与约定】

对于 $20 \%$ 的评测用例,$n \leq 10$;

对于 $60 \%$ 的评测用例,$n \leq 20$;

对于所有评测用例,$1 \leq n \leq 30$,$1 \leq A_i \leq 10^9$,$1 \leq m \leq 10^9$。

## 前情提要🍉(bushi)

<iframe src="https://player.bilibili.com/player.html?aid=448136466&bvid=BV14j41117D6&cid=1259813341&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 1000px; left: 0; top: 0;"> </iframe>

## 解法
(瞅了一眼标签,折半搜索?跟生瓜蛋子一样不熟)
西瓜对半切/不切,不妨乘二,当作一个西瓜/两个西瓜来做
先贪心排序,优先找大瓜。
然后深度优先搜索,根据质量上下超限(小于要求质量用后缀和判断)、劈瓜数已大于当前最小答案来剪枝。
```cpp
#include<bits/stdc++.h>
using ll=long long;
const int N=32;
using namespace std;
int n,mid,m;//西瓜总数量,一半数量,要求总质量
int ans=INT_MAX;//答案
int a[N];//有一个人前来买瓜
ll sum[N];//后缀和,用来剪枝
void dfs(int i,int num,int weight){//到哪个瓜了,劈了多少瓜,买了几斤瓜
if(weight>=m){
if(weight==m)
ans=min(ans,num);
return;
}
if(i==n||num>=ans||weight+sum[i]<m)//超总数的递归出口,超过最小劈瓜数递归,质量不足以达到要求数剪枝
return;
dfs(i+1,num,weight+(a[i]<<1));//卖你生瓜蛋子
dfs(i+1,num+1,weight+a[i]);//你**劈我瓜是吧!
dfs(i+1,num,weight);//你要不要吧
}
int main() {
scanf("%d%d",&n,&m);
mid=n/2;
m*=2;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n,greater<>());//从大到小贪心
for(int i=n-1;i>=0;i--){
sum[i]=sum[i+1]+(a[i]<<1);//求后缀和
}
dfs(0,0,0);
if(ans==INT_MAX)
ans=-1;
printf("%d\n",ans);
return 0;
}


[蓝桥杯 2023 省 A] 网络稳定性

题目描述

有一个局域网,由 个设备和 条物理连接组成,第 条连接的稳定性为

对于从设备 到设备 的一条经过了若干个物理连接的路径,我们记这条路径的稳定性为其经过所有连接中稳定性最低的那个。

我们记设备 到设备 之间通信的稳定性为 的所有可行路径的稳定性中最高的那一条。

给定局域网中的设备的物理连接情况,求出若干组设备 之间的通信稳定性。如果两台设备之间不存在任何路径,请输出

输入格式

输入的第一行包含三个整数 ,分别表示设备数、物理连接数和询问数。

接下来 行,每行包含三个整数 ,分别表示 之间有一条稳定性为 的物理连接。

接下来 行,每行包含两个整数 ,表示查询 之间的通信稳定性。

输出格式

输出 行,每行包含一个整数依次表示每个询问的答案。

样例 #1

样例输入 #1

1
2
3
4
5
6
7
8
5 4 3
1 2 5
2 3 6
3 4 1
1 4 3
1 5
2 4
1 3

样例输出 #1

1
2
3
-1
3
5

提示

【评测用例规模与约定】

对于 的评测用例,

对于 的评测用例,

对于所有评测用例,

解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
```

---

# [蓝桥杯 2023 省 A] 异或和之和

## 题目描述

给定一个数组 $A_i$,分别求其每个子段的异或和,并求出它们的和。或者说,对于每组满足 $1 \leq L \leq R \leq n$ 的 $L,R$,求出数组中第 $L$ 至第 $R$ 个元素的异或和。然后输出每组 $L,R$ 得到的结果加起来的值。

## 输入格式

输入的第一行包含一个整数 $n$ 。

第二行包含 $n$ 个整数 $A_i$,相邻整数之间使用一个空格分隔。

## 输出格式

输出一行包含一个整数表示答案。

## 样例 #1

### 样例输入 #1

5
1 2 3 4 5

1
2
3

### 样例输出 #1


39
```

提示

【评测用例规模与约定】

对于 的评测用例,

对于 的评测用例,;

对于所有评测用例,

解法

异或的性质


(没什么,复习一下)

设序列为,从的异或和为, 从的异或前缀和为,则

即对~个前缀和,两两异或的和。
按二进制位数相加
```cpp

include

using namespace std;
using ll=long long;
const int N=(1e5)+5;
int n;
int f[N];
ll res=0ll;
int main() {
int a;
scanf(“%d”,&n);
for(int i=1;i<=n;i++){
scanf(“%d”,&a);
f[i]=f[i-1]^a;
}
int bei=1;//位权
for(int i=0;i<=20;i++){//20位二进制
int numof1=0,numof0=1;
ll sum=0;
for(int j=1;j<=n;j++){
if(f[j]&1){//最低一位1^0=1
sum+=numof0;
numof1++;
}else{//最低一位0^1=1
sum+=numof1;
numof0++;
}
f[j]>>=1;//下一位
}
res+=sum*bei;
bei<<=1;//位权翻倍
}
cout<<res<<endl;
return 0;
}
```..

原题与参考:
https://www.luogu.com.cn/problem/P3865
https://oi-wiki.org/ds/sparse-table/

模板题题意

给定个数,有个询问,对于每个询问,你需要回答区间中的最大值

简介

ST 表(Sparse Table,稀疏表)是用于解决可重复贡献问题的数据结构。

可重复贡献问题:对于运算opt,满足,如

比起线段树,只能静态查询,不能修改信息。
使用倍增思想,时间复杂度:

步骤

对数初处理

1
2
3
4
int lg[N];//对数表
for(int i=2;i<=n;i++){//初始化对数表
lg[i]=lg[i>>1]+1;
}

转移方程

为数序列,为区间的最大值,则

这里有张动图来着

1
2
3
4
5
6
7
int w=1;
for(int j=1;j<=lg[n];j++){
for(int i=1;i<=n-w;i++){
f[i][j]=max(f[i][j-1],f[i+w][j-1]);
}
w<<=1;
}

询问

询问时,把区间分成,其中
即查询
这里有张动图来着

1
2
3
scanf("%d%d",&l,&r);
s=lg[r-l+1];
printf("%d\n",max(f[l][s],f[r-(1<<s)+1][s]));

总代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<bits/stdc++.h>
const int N=(1e5)+5;
using namespace std;
int n,m;//数列的长度,询问的个数
int f[N][22];//区间[i,i+2^j-1]的最大值
int lg[N];//对数表
int main() {
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++){//初始化对数表
lg[i]=lg[i>>1]+1;
}
for(int i=1;i<=n;i++){
scanf("%d",&f[i][0]);
}
int w=1;
for(int j=1;j<=lg[n];j++){
for(int i=1;i<=n-w;i++){
f[i][j]=max(f[i][j-1],f[i+w][j-1]);
}
w<<=1;
}
int l,r,s;
while(m--){
scanf("%d%d",&l,&r);
s=lg[r-l+1];
printf("%d\n",max(f[l][s],f[r-(1<<s)+1][s]));
}
return 0;
}

【模板】并查集

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 ,表示共有 个元素和 个操作。

接下来 行,每行包含三个整数

时,将 所在的集合合并。

时,输出 是否在同一集合内,是的输出
Y ;否则输出 N

输出格式

对于每一个 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N

样例 #1

样例输入 #1

1
2
3
4
5
6
7
8
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

样例输出 #1

1
2
3
4
N
Y
N
Y

提示

对于 的数据,

对于 的数据,

对于 的数据,

对于 的数据,

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<bits/stdc++.h>
const int N=(1e5)+5;
using namespace std;
struct dsu{
vector<int> parent,size;
//Single-argument constructors must be marked explicit to avoid unintentional implicit conversions
explicit dsu(int s):parent(s+1),size(s+1,1){
iota(parent.begin(),parent.end(),0);//1,2,...,s-1
}
// 查询
int find(int x){
return parent[x]==x?x:parent[x]=find(parent[x]);
}
// 启发式合并
void unite(int x,int y){
x=find(x),y=find(y);
if(x==y)
return;
if(size[x]<size[y])
swap(x,y);
parent[y]=x;
size[x]+=size[y];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int n,m;
cin>>n>>m;
dsu s=dsu(n);
int x,y,z;
for(int i=0;i<m;i++){
cin>>z>>x>>y;
if(z==1){
s.unite(x,y);
}
else{
if(s.find(x)==s.find(y)){
cout<<'Y'<<endl;
}
else{
cout<<'N'<<endl;
}
}
}
return 0;
}

并查集介绍

并查集是一种数据结构,用于处理一些不交集合并及查询的问题。
并查集是森林,每个集合用一个树结构表示,树的根节点代表整个集合。

结构

1
2
3
4
5
6
7
8
struct dsu{
vector<int> parent,size;
// 单参数构造函数必须使用 explicit 关键字进行标记,以避免发生不必要的隐式类型转换。在<numeric>库里
//这里节点是从1开始到s
explicit dsu(int s):parent(s+1),size(s+1,1){
iota(parent.begin(),parent.end(),0);//1,2,...,s-1
}
};

合并

动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 简单合并
void simple_unite(int x, int y) {
parent[find(x)] = find(y);
}
// 启发式合并
void unite(int x,int y){
x=find(x),y=find(y);
if(x==y)//在同一个集合里
return;
if(size[x]<size[y])
swap(x,y);
parent[y]=x;
size[x]+=size[y];
}

查询

动画

找到了根节点,就顺便把父节点都更新成根节点

1
2
3
int find(int x){
return parent[x]==x?x:parent[x]=find(parent[x]);
}

删除

1
2
3
4
void erase(int x) {
--size[find(x)];
parent[x] = x;
}

移动

1
2
3
4
5
6
void move(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return;
parent[x] = fy;
--size[fx], ++size[fy];
}

总代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct dsu{
vector<int> parent,size;
explicit dsu(int s):parent(s+1),size(s+1,1){
iota(parent.begin(),parent.end(),0);//1,2,...,s-1
}
// 查询
int find(int x){
return parent[x]==x?x:parent[x]=find(parent[x]);
}
// 合并
void simple_unite(int x, int y) {
parent[find(x)] = find(y);
}
// 启发式合并
void unite(int x,int y){
//找到根结点
x=find(x),y=find(y);
//在同一个集合里
if(x==y)
return;
if(size[x]<size[y])
swap(x,y);
parent[y]=x;
size[x]+=size[y];
}
//删除
void erase(int x) {
--size[find(x)];
parent[x] = x;
}
//移动
void move(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return;
parent[x] = fy;
--size[fx], ++size[fy];
}
};


[蓝桥杯 2024 省 A] 艺术与篮球

题目描述

小蓝出生在一个艺术与运动并重的家庭中。
妈妈是位书法家,她希望小蓝能通过练习书法,继承她的艺术天赋,并练就一手好字。爸爸是一名篮球教练,他希望小蓝能通过篮球锻炼身体,培养运
动的激情和团队合作的精神。
为了既满足妈妈的期望,又不辜负爸爸的心意,小蓝决定根据日期的笔画数来安排自己的练习。首先,他会将当天的日期按照 YYYYMMDD 的格式
转换成一个 位数,然后将这 位数对应到汉字上,计算这些汉字的总笔画数。如果总笔画数超过 ,他就去练习篮球;如果总笔画数不超过 ,他就去练习书法。
例如,在 日这天,日期可表示为一个 位数字 ,其转换为汉字是“二零二四零一零一”。日期的总笔画数为 ,因此在这天,小蓝会去练习书法。
以下是汉字的笔画数对照表:

汉字 笔画数 汉字 笔画数

现在,请你帮助小蓝统计一下,在 日到
这段时间内,小蓝有多少天是在练习篮球?

输入格式

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

输出格式

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

解法

模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<bits/stdc++.h>
const int N=(1e5)+5;
using namespace std;
int bh[]={13,1,2,3,5,4,4,2,2,2};
int bihua[32]={0};
int month[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int res=0;
int main() {
int ans=0;
for(int i=0;i<=31;i++){
bihua[i]=bh[i/10]+bh[i%10];
}
int y;
for(y=2000;y<=2023;y++){
int year_days=bihua[y/100]+bihua[y%100];
if(y%4==0)
month[2]=29;
else
month[2]=28;
for(int m=1;m<=12;m++){
int mon_days=year_days+bihua[m];
for(int d=1;d<=month[m];d++){
if(mon_days+bihua[d]>50)
ans++;
}
}
}
month[2]=29;
month[4]=13;
int year_days=bihua[y/100]+bihua[y%100];
for(int m=1;m<=4;m++){
int mon_days=year_days+bihua[m];
for(int d=1;d<=month[m];d++){
if(mon_days+bihua[d]>50)
ans++;
}
}
cout<<ans<<endl;
return 0;
}
//3228

[蓝桥杯 2024 省 A] 五子棋对弈

题目描述

“在五子棋的对弈中,友谊的小船说翻就翻?” 不!对小蓝和小桥来说,五子棋不仅是棋盘上的较量,更是心与心之间的沟通。这两位挚友秉承着 “友谊第一,比赛第二” 的宗旨,决定在一块 的棋盘上,用黑白两色的棋子来决出胜负。但他们又都不忍心让对方失落,于是决定用一场和棋(平局) 作为彼此友谊的见证。
比赛遵循以下规则:

  1. 棋盘规模:比赛在一个 的方格棋盘上进行,共有 个格子供下棋使用。
  2. 棋子类型:两种棋子,黑棋与白棋,代表双方。小蓝持白棋,小桥持黑棋。
  3. 先手规则:白棋(小蓝)具有先手优势,即在棋盘空白时率先落子(下棋)。
  4. 轮流落子:玩家们交替在棋盘上放置各自的棋子,每次仅放置一枚。
  5. 胜利条件:率先在横线、竖线或斜线上形成连续的五个同色棋子的一方获胜。
  6. 平局条件:当所有 个棋盘格都被下满棋子,而未决出胜负时,游戏以平局告终。

在这一设定下,小蓝和小桥想知道,有多少种不同的棋局情况(终局不同看成不同情况,终局相同而落子顺序不同看成同一种情况),既确保棋盘下满又保证比赛结果为平局。

输入格式

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

输出格式

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

解法

dfs,模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<bits/stdc++.h>
const int N=(1e5)+5;
using namespace std;
using ll=unsigned long long;
int arr[26];
/*
* 1 2 3 4 5
* 6 7 8 9 10
* 11 12 13 14 15
* 16 17 18 19 20
* 21 22 23 24 25
*/
ll ans=0;
void dfs(int d,int b){
if(b>13||d-b>13)
return;
if(d==26){
bool ok=false;
for(int i=7;i<=25;i+=6)
if(arr[1]!=arr[i]){
ok=true;
break;
}
if(ok)
ans++;
return;
}
for(int i=0;i<=1;i++){
arr[d]=i;
if(d%5==0){
bool ok=false;
for(int j=1;j<=4;j++){//0,1,2,3,4
if(arr[d]!=arr[d-j]){
ok=true;
break;
}
}
if(!ok)
continue;
}
if(d==21){
bool ok=false;
for(int j=9;j<=21;j+=4){//5,9,13,17,21
if(arr[5]!=arr[j]){
ok=true;
break;
}
}
if(!ok)
continue;
}
if(d>=21){
bool ok=false;
for(int j=5;j<=20;j+=5){
if(arr[d]!=arr[d-j]){
ok=true;
break;
}
}
if(!ok)
continue;
}
dfs(d+1,b+i);
}
}
int main() {
dfs(1,0);
cout<<ans<<endl;
}
//3126376


[蓝桥杯 2024 省 A] 训练士兵

题目描述

在蓝桥王国中,有 名士兵,这些士兵需要接受一系列特殊的训练,以提升他们的战斗技能。对于第 名士兵来说,进行一次训练所需的成本为 枚金币,而要想成为顶尖战士,他至少需要进行 次训练。
为了确保训练的高效性,王国推出了一种组团训练的方案。该方案包含每位士兵所需的一次训练,且总共只需支付 枚金币(组团训练方案可以多次购买,即士兵可以进行多次组团训练)。
作为训练指挥官,请你计算出最少需要花费多少金币,才能使得所有的士兵都成为顶尖战士?

输入格式

输入的第一行包含两个整数 ,用一个空格分隔,表示士兵的数量和进行一次组团训练所需的金币数。
接下来的 行,每行包含两个整数 ,用一个空格分隔,表示第 名士兵进行一次训练的金币成本和要成为顶尖战士所需的训练次数。

输出格式

输出一行包含一个整数,表示使所有士兵成为顶尖战士所需的最少金币数。

样例 #1

样例输入 #1

1
2
3
4
3 6
5 2
2 4
3 2

样例输出 #1

1
16

提示

花费金币最少的训练方式为:进行 次组团训练,花费 枚金币,此时士兵 已成为顶尖战士;再花费 枚金币,让士兵 进行两次训练,成为顶尖战士。总花费为

对于 的评测用例,

对于所有评测用例,

解法

按照需要训练数,从多往少贪心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
#define pay second
#define num first
using namespace std;
using ll = long long;
#define int long long // 十年oi一场空,不开 long long见祖宗
typedef pair<int, int> pp;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr); // 正式比赛不要用这两行,用scanf和printf
int n, s;
cin >> n >> s;
vector<pair<int, int>> arr(n);
for (int i = 0; i < n; i++)
{
cin >> arr[i].pay >> arr[i].num;
}
sort(arr.begin(), arr.end());
int single_paid = 0, sum = 0, i;
for (i = n - 1; i >= 0; i--)
{
single_paid += arr[i].pay;
if (single_paid >= s) // 从第i个开始往前,集体训练更便宜,使用集体训练
{
break;
}
}
int c;
if (i == -1) // 全都是单独训练便宜
{
i = 0;
c = 0;
}
else
{
c = arr[i].num;
}
sum = c * s;
for (int j = i; j < n; j++)
{
sum += arr[j].pay * (arr[j].num - c);
}
cout << sum << endl;
return 0;
}


[蓝桥杯 2024 省 A] 团建

题目描述

小蓝正在和朋友们团建,有一个游戏项目需要两人合作,两个人分别拿到一棵大小为 的树,树上的每个结点上有一个正整数权值。
两个人需要从各自树的根结点 出发走向某个叶结点,从根到这个叶结点的路径上经过的所有结点上的权值构成了一个正整数序列,两人的序列的最长公共前缀即为他们的得分。给出两棵树,请计算两个人最多的得分是多少。

输入格式

输入的第一行包含两个正整数 ,用一个空格分隔。
第二行包含 个正整数 ,相邻整数之间使用一个空格分隔,其中 表示第一棵树结点 上的权值。
第三行包含 个正整数 ,相邻整数之间使用一个空格分隔,其中 di 表示第二棵树结点 上的权值。
接下来 行,每行包含两个正整数 表示第一棵树中包含一条
之间的边。
接下来 行,每行包含两个正整数 表示第二棵树中包含一条
之间的边。

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

1
2
3
4
5
2 2
10 20
10 30
1 2
2 1

样例输出 #1

1
1

样例 #2

样例输入 #2

1
2
3
4
5
6
7
8
9
10
5 4
10 20 30 40 50
10 40 20 30
1 2
1 3
2 4
3 5
1 2
1 3
3 4

样例输出 #2

1
2

提示

对于 的评测用例,
对于所有评测用例,,对于任意结点,其儿子结点的权重互不相同。

解法

对两个树进行dfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
#define N 200005
using namespace std;
using ll = long long;

int n[2], res = 0;

vector<vector<int>> node(2);
vector<vector<vector<int>>> g(2);
vector<vector<bool>> vis(2, vector<bool>(N));

void dfs(int u, int v, int d)
{
res = max(res, d);
map<int, int> mp; // tree2 value in tree1 value?不用map,只用双重循环过不了
for (int i = 0, l = g[0][u].size(); i < l; i++)
{
int uu = g[0][u][i];
if (vis[0][uu])
continue;
mp[node[0][uu]] = uu;
}
for (int j = 0, s = g[1][v].size(); j < s; j++)
{
int vv = g[1][v][j];
if (vis[1][vv])
continue;
if (mp.count(node[1][vv]) > 0)
{
int uu = mp[node[1][vv]];
vis[0][uu] = vis[1][vv] = true;
dfs(uu, vv, d + 1);
}
}
}
signed main()
{
for (int i = 0; i < 2; i++)
scanf("%d", &n[i]);
for (int i = 0; i < 2; i++)
{
node[i].resize(n[i] + 1);
for (int j = 1; j <= n[i]; j++)
scanf("%d", &node[i][j]);
}
int u, v;
for (int i = 0; i < 2; i++)
{
g[i].resize(n[i] + 1);
for (int j = 1; j < n[i]; j++)
{
scanf("%d%d", &u, &v);
g[i][u].push_back(v);
g[i][v].push_back(u);
}
}
vis[0][1] = vis[1][1] = true;
if (node[0][1] == node[1][1])
dfs(1, 1, 1);
printf("%d\n", res);
return 0;
}


[蓝桥杯 2024 省 A] 成绩统计

题目描述

小蓝的班上有 个人,一次考试之后小蓝想统计同学们的成绩,第 名同学的成绩为 。当小蓝统计完前 名同学的成绩后,他可以从 中选出任意 名同学的成绩,计算出这 个成绩的方差。小蓝至少要检查多少个人的成
绩,才有可能选出 名同学,他们的方差小于一个给定的值
提示: 个数 的方差 定义为:,其中 表示
的平均值,

输入格式

输入的第一行包含三个正整数 ,相邻整数之间使用一个空格分隔。

第二行包含 个正整数 ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。如果不能满足条件,输出

样例 #1

样例输入 #1

1
2
5 3 1
3 2 5 2 3

样例输出 #1

1
4

提示

检查完前三名同学的成绩后,只能选出 ,方差为

检查完前四名同学的成绩后,可以选出 ,方差为 ,所以答案为

对于 的评测用例,保证
对于 的评测用例,保证
对于所有评测用例,保证

解法

前缀和, 二分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>
#define N 100005
using namespace std;
using ll = long long;
ll n, k, t, kt;
ll a[N], v[N];
ll sum[N], sum2[N];
inline ll pow2(ll x)
{
return x * x;
}
bool check(int x)
{
memcpy(v + 1, a + 1, x * sizeof(ll));
sort(v + 1, v + 1 + x);
for (int i = 1; i <= x; i++)
{
sum[i] = sum[i - 1] + v[i];
sum2[i] = sum2[i - 1] + pow2(v[i]);
}
for (int i = k; i <= x; i++)
{
double ds = 1.0 * sum[i] - sum[i - k];
if ((1.0 * sum2[i] - sum2[i - k]) - (ds / k) * ds < kt)
{
return true;
}
}
return false;
}
signed main()
{
scanf("%lld%lld%lld", &n, &k, &t);
kt = k * t;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
}
int l = k, r = n, res = -1;
while (l <= r)
{
int mid = l + ((r - l) >> 1);
if (check(mid))
{
res = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
printf("%d\n", res);
return 0;
}

[蓝桥杯 2024 省 A] 因数计数

题目描述

小蓝随手写出了含有 个正整数的数组 ,他发现可以轻松地算出有多少个有序二元组 满足 的一个因数。因此他定义一个整数对 是一个整数对 的“因数”当且仅当 分别是 的因数。他想知道有多少个有序四元组 满足 的因数,其中 互不相等。

输入格式

输入的第一行包含一个正整数
第二行包含 个正整数 ,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。

样例 #1

样例输入 #1

1
2
5
3 6 2 2 7

样例输出 #1

1
4

提示

四元组 的因子;
四元组 的因子;
四元组 的因子;
四元组 的因子。

对于 的评测用例,
对于 的评测用例,
对于所有评测用例,

解法

样例输入:
找到不同的两对因数

1
2
5
3 6 2 2 7

能找到(1,2), (3,2), (4,2), (3,4), (4,3)五对因数(对于(i,j), i≤j)
四元组,满足四个元素不重叠的两组因数,有{(1,2),(3,4)}, {(1,2),(4,3)},{(3,4),(1,2)},{(4,3),(1,2)}

先写了个直接按题意的代码void baoli(),造出测试例。
再尝试写了一个复杂度的代码,会有一半的测试例TLE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*尝试的笨代码*/
#include <bits/stdc++.h>
#define N 100005
using namespace std;
using ll = long long;
ll res = 0ll;
int n; // 数据数量
int a[N]; // 数据
ll e_sum, e_same; // 边的总数量,端点对应数值相同的边的总数量(双向边总数量)
ll e[N]; // 连接边的数量
void baoli(); // 暴力验证
inline ll c2(int a) // 组合数,n*(n+1)/2,因为最后结果要*2就抵消了
{
return a * (a - 1);
}
signed main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
// baoli();
// return 0;
sort(a, a + n);
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (a[j] % a[i] == 0)
{

if (a[i] == a[j])
{
e[i] += 2;
e[j] += 2;
e_sum += 2;
e_same += 2;
}
else
{
e[i]++;
e[j]++;
e_sum++;
}
}
}
}
res = c2(e_sum) + e_same;
for (int i = 0; i < n; i++)
{
// cout << e[i] << ' ';
res -= c2(e[i]);
}
printf("%lld\n", res);
return 0;
}
// 以下是纯暴力验证
int baoli_ans = 0;
int t[4];
void baoli_dfs(int d)
{
if (d == 4)
{
for (int i = 0; i < 4; i++)
for (int j = i + 1; j < 4; j++)
if (t[i] == t[j])
return;
if (a[t[1]] % a[t[0]] == 0 && a[t[3]] % a[t[2]] == 0)
{
baoli_ans++;
}
return;
}
for (int i = 0; i < n; i++)
{
t[d] = i;
baoli_dfs(d + 1);
}
}
void baoli()
{
baoli_dfs(0);
cout << baoli_ans << endl;
}
/*
input:
10
1 2 2 3 3 3 4 5 6 6
//ouput: 564
*/

说明至少思路是对的,只是爆时间了👉👈。换一种方式找它们之间的因数关系。
设点输入的数量为,最大的因子为是点和多少个点有因数关系。把两个端点是因数关系的称作一条边。两条样的边称为一对边
对每一个因子,乘以倍数,获得它的倍数,直到超过为止。这样,,点x和y之间连边,它们增加边的数量均为
时间复杂度:,比好。

一个答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
#define N 100005
#define M 100000
using namespace std;
using ll = unsigned long long;

ll n, m; // 数据数量,不重复的数据的数量
ll num[N]; // 不重复数据排列
ll cnt[N]; // 桶
ll e[N]; // 连接到的值不一样的边
ll res; // 结果
inline ll c2(ll a) // 组合数,n*(n+1)/2,因为最后结果要*2就抵消了
{
return a * (a - 1);
}
signed main()
{
scanf("%d", &n);
ll a;
for (register ll i = 0; i < n; i++)
{
scanf("%d", &a);
cnt[a]++;
}
for (register ll i = 1; i <= M; i++)
{
if (cnt[i] == 0)
continue;
num[m++] = i;
res += c2(cnt[i]);
for (register ll j = i * 2; j <= M; j += i)
{
e[i] += cnt[j];
e[j] += cnt[i];
res += cnt[i] * cnt[j];
}
}
res = c2(res);
for (register ll i = 0; i < m; i++)
{
res -= c2(e[num[i]] + 2 * (cnt[num[i]] - 1)) * cnt[num[i]];
res += c2(cnt[num[i]]);
}
cout << res<< endl;
return 0;
}


[蓝桥杯 2024 省 A] 零食采购

题目描述

小蓝准备去星际旅行,出发前想在本星系采购一些零食,星系内有 颗星球,由 条航路连接为连通图,第 颗星球卖第 种零食特产。小蓝想出了 个采购方案,第 个方案的起点为星球 ,终点为星球 ,对于每种采购方案,小蓝将从起点走最短的航路到终点,并且可以购买所有经过的星球上的零食(包括起点终点),请计算每种采购方案最多能买多少种不同的零食。

输入格式

输入的第一行包含两个正整数 ,用一个空格分隔。
第二行包含 个整数 ,相邻整数之间使用一个空格分隔。
接下来 行,第 行包含两个整数 ,用一个空格分隔,表示一条
航路将星球 相连。
接下来 行,第 行包含两个整数 ,用一个空格分隔,表示一个采购方案。

输出格式

输出 行,每行包含一个整数,依次表示每个采购方案的答案。

样例 #1

样例输入 #1

1
2
3
4
5
6
7
4 2
1 2 3 1
1 2
1 3
2 4
4 3
1 4

样例输出 #1

1
2
3
2

提示

第一个方案路线为 ,可以买到第 种零食;
第二个方案路线为 ,可以买到第 种零食。

对于 20% 的评测用例,
对于所有评测用例,

解法

使用最近公共祖先LCA板子,在其中加上状态压缩DP
我博客里的lca: https://misaka-xxw.github.io/2025/02/01/lca/

首先,注意审题,题目里说n个节点,n-1条边,连通图,所以这是一个。任意选中一个根节点,那么点s到点e的最短路径,就是从s往上走到根节点,再往下走到点e。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<bits/stdc++.h>
using namespace std;
const int N=(5e5)+5;

int n,s,e;//结点数,起点,终点
vector<int> g[N];//树
int lg[N];//对数表
int c[N];//每个结点对应的食物
int depth[N],father[N][31],dp[N][31];//深度,第i结点的2^j个祖先,以及这条链路对应相应的食物购买情况
void init_lg() {
for (int i = 2; i <= n; ++i) {
lg[i] = lg[i>>1] + 1;
}
}
//利用补码的性质,找二进制里最后一个1
inline int lastof1(int x){
return x & -x;
}
//统计二进制里有多少1
inline int numof1(int x){
int a=0;
while(x){
x-= lastof1(x);
a++;
}
return a;
}
// 深度优先搜索,计算每个节点的父节点倍增表
void dfs(int me,int fa) {
for (int i = 1; i <= lg[depth[me]]; ++i){
dp[me][i]=dp[father[me][i - 1]][i - 1]|dp[me][i - 1];
father[me][i] = father[father[me][i - 1]][i - 1];
}
for (int son : g[me])
if (son!=fa){
depth[son] = depth[me] + 1;
father[son][0] = me;
dp[son][0]=c[son]|c[me];
dfs(son,me);
}
}
// 查询x和y的最低公共祖先用lca算法,改变
int lca(int x,int y){
int res=c[x]|c[y];
if(depth[x]<depth[y])
swap(x,y);
while(depth[x]>depth[y]){
int delta=lg[depth[x]-depth[y]];
res|=dp[x][delta];
x=father[x][delta];
}
if(x!=y){
for(int i=lg[depth[x]];i>=0;--i){
if(father[x][i]!=father[y][i]){
res|=(dp[x][i]|dp[y][i]);
x=father[x][i];
y=father[y][i];
}
}
res|=dp[x][0];
}
return numof1(res);
}
int main() {
int u,v,q;
scanf("%d%d",&n,&q);
init_lg();
for(int i=1;i<=n;i++){
scanf("%d",&u);
c[i]=(1<<u);
}
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,1);
while(q--){
scanf("%d%d",&s,&e);
printf("%d\n",lca(s,e));
}
return 0;
}

[蓝桥杯 2024 省 A] 封印宝石

题目描述

在一次探险中,勇者小蓝发现了 颗闪烁着奇异光芒的宝石,每颗宝石都蕴含着魔法能量,分别记作 。小蓝计划用 个特制的魔法盒子来封
印这些宝石,防止其魔法能量被滥用。
封印宝石会消耗小蓝的体力,具体地,将第 颗宝石放入第 个盒子会消耗小蓝 点体力(注:需满足 才能将第 颗宝石放入第 个盒子进行有效的封印)。小蓝也可以选择将魔法盒留空,以保存体力供后续使用。
此外,为了避免魔力相冲,每个盒子最多存放一颗宝石(每个宝石也只能放进一个盒子),且任意两个相邻盒子不能存放魔力值相同的宝石,相邻的盒子允许同时为空。
小蓝初始的体力值为 。在不超出体力限制的条件下,小蓝希望找出一种宝石的放置方法,使得宝石的魔力值在这 个盒子中的排列顺序具有最大的字典序(注:未放置宝石的盒子在此序列中记为 )。
作为勇者小蓝的追随者,请你帮他找出这一放置宝石的方法。

字典序的解释: 在本题中,字典序的大小是按照宝石的魔力值进行比较的。对于两个长度同为 的魔力值序列 ,如果存在一个位置 ,使得 对所有 成立,但是 ,则序列 在字典序上小于序列
反之,如果 ,则序列 在字典序上大于序列 。如果不存在这样的 ,则序列 和序列 的字典序相等。

输入格式

输入的第一行包含两个整数 ,用一个空格分隔,分别表示宝石的数量和小蓝的初始体力值。
第二行包含 个整数 ,相邻整数之间使用一个空格分隔,分别表示每颗宝石的魔法能量值。

输出格式

输出一行包含 个整数,相邻整数之间使用一个空格分隔,表示每个魔法盒中宝石的魔法能量值。如果某个魔法盒为空,则对应位置输出

样例 #1

样例输入 #1

1
2
3 3
1 3 2

样例输出 #1

1
3 2 -1

提示

在开始放置宝石之前,体力为 ,宝石在盒子中的排列为

  1. 将第 个宝石放进第 个盒子,得到 ,体力剩余
  2. 将第 个宝石放进第 个盒子,得到 ,体力剩余

最后宝石在盒子中的排列为 。显然,没有比这更优的放置方法。

对于 的评测用例,
对于所有评测用例,

解法

贪心+线段树
我博客里的线段树:https://misaka-xxw.github.io/2025/02/02/segment-tree/

解释

给定个数放到个位置上,放到位置上消耗个代价,合理放置这些数,让它们在代价范围内有最大的字典序。
要求:

  • 可以放在的位置区间
  • 位置可以放置区间的数
  • 可以有空的位置,相邻位置不能有相同的数。
  • 字典序的解释:两个排列方法,对于最靠前的放置了不相等的数的位置,放更大的数的那个字典序更大。没有放数就是-1。如:[5,4,3,…]比[5,4,2,…]大

贪心

  • 优先将更大的数放置到更靠前的位置
  • 对于相等的数,选更靠前
  • 两个位置不能同时放一样的数

线段树

  • 作用:区间内最大值和次大值的查询和更新
  • 从1遍历到n,查询区间内的最大值,放在位置上。
    • 如果和上一个答案相同,选次大值。
    • 使用了宝石,就要更新线段树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include<bits/stdc++.h>

using namespace std;
const int N = (1e5) + 5;

struct node {
int num; // 数值
int pos; // 第几个数值
node() : num(-1), pos(0) {}

node(int a, int b) : num(a), pos(b) {}

bool operator>(const node &other) const {
return num != other.num ? num > other.num : pos < other.pos;
}
} node0;//空结点
typedef pair<node, node> maxs;//最大值和次大值,对应first和second
int n, m;//数量,代价,当前搜索
int a[N];//宝石数值
int loc[N];//判断叶子节点位置与线段树位置的对应关系
int ans[N];//答案,放置方法
maxs t[4 * N];//线段树,最大值和次大值
maxs res;//存放find_max时的最大值和次大值

// 更新某个节点的最大值和次大值,可能稍微快一点
void update_max(maxs &cur, const maxs l, const maxs r) {
//根据l和r,更新cur的最大值和次大值
if (l.first.num > r.first.num) {//左最大值大
cur.first = l.first;
if (l.second.num >= r.first.num)
cur.second = l.second;
else
cur.second = r.first;
} else if (l.first.num < r.first.num) {//右最大值大
cur.first = r.first;
if (l.first.num >= r.second.num)
cur.second = l.first;
else
cur.second = r.second;
} else {//左右最大值相同
cur.first = l.first;
if (l.second.num != cur.first.num && l.second.num >= r.second.num)
cur.second = l.second;
else if (r.second.num < cur.first.num)
cur.second = r.second;
else
cur.second = node0;
}
}

//效果一样的函数,似乎稍微慢一点点
void update_max2(maxs &cur, const maxs l, const maxs r) {
//根据l和r,更新cur的最大值和次大值
node tmp[] = {l.first, l.second, r.first, r.second};
sort(tmp, tmp + 4, greater<>());//从大到小排
cur.first = tmp[0];
for (int i = 1; i < 4; i++) {
if (cur.first.num != tmp[i].num) {
cur.second = tmp[i];
return;
}
}
cur.second = node0;
}

//建立线段树
void build(int l, int r, int p) {
if (l == r) {
t[p].first = node(a[l], l);
t[p].second = node0;
loc[l] = p;
return;
}
int mid = l + ((r - l) >> 1), lc = p << 1, rc = lc | 1;
build(l, mid, lc);
build(mid + 1, r, rc);
update_max(t[p], t[lc], t[rc]);
}

//查找最大值
void find_max(int l, int r, int i, int j, int p) {
if (l <= i && j <= r) {//找到了合适的区间
update_max(res, res, t[p]);
return;
}
int lc = p << 1, rc = lc | 1;
int mid = i + ((j - i) >> 1);
if (l <= mid) find_max(l, r, i, mid, lc);
if (mid < r) find_max(l, r, mid + 1, j, rc);
}
// 自底而上,修改被影响节点的最大值和次大值
void modify(int pos) {
int p = loc[pos];
t[p].first = node0;
t[p].second = node0;
for (p >>= 1; p >= 1; p >>= 1) {
update_max(t[p], t[p << 1], t[(p << 1) | 1]);
}
}

int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
memset(ans, -1, sizeof(ans));//初始化为-1,盒子为空
build(1, n, 1);//建立线段树
for (int i = 1; i <= n; i++) {
res = {node0, node0};
find_max(i, min(i + m, n), 1, n, 1);
int pos;
if (res.first.pos == 0)//最大值是一个空节点
continue;
else if (res.first.num == ans[i - 1]) {//最大值跟前一个盒子相同撞了,找次大值
if (res.second.pos == 0)//次大值是一个空节点
continue;
ans[i] = res.second.num;//选次大值
pos = res.second.pos;
} else {
ans[i] = res.first.num;//选最大值
pos = res.first.pos;
}
m -= (pos - i);
modify(pos);
}
for (int i = 1; i < n; i++) {
printf("%d ", ans[i]);
}
printf("%d\n", ans[n]);
return 0;
}

0%