官方题解 | 巅峰赛#37
2026-08-18 10:10:36
发布于:浙江
巅峰赛37题解
本次题目的总体难度如下,各位选手可以借此评估一下自身的技术水平
| 题目编号 | 题目标题 | 难度 |
|---|---|---|
| T1 | 午枫的幸运名单 | 普及/提高- |
| T2 | 宝藏密码 | 普及/提高- |
| T3 | 午枫的宝藏 | 普及/提高- |
| T4 | 午枫的罗盘 | 普及/提高- |
| T5 | 午枫的航海日志 | 普及+/提高 |
| T6 | 午枫的密码本 | 普及+/提高 |
T1 午枫的幸运名单
题意简述
给定一份长度为 的名单以及午枫的名字 。
依次查看名单中的名字,如果找到与 相同的名字,则输出其所在位置(排名);如果遍历完整个名单仍未找到,则输出 -1。
解题思路
直接模拟查找即可。
读入午枫的名字后,依次读取名单中的每个名字,判断是否与目标名字相同。由于名单中的名字互不相同,因此最多只会匹配一次。
记录匹配到的位置,最后输出对应排名;若始终未匹配到,则输出 -1。
时间复杂度为 O(n)。
参考代码
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
string target;
cin >> n >> target;
int answer = -1;
for (int i = 1; i <= n; i++) {
string name;
cin >> name;
if (name == target) {
answer = i;
}
}
cout << answer << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
T2 宝藏密码
题意简述
给定 条形如 的方程,但 与输入的三个数 对应关系未知(共有 种可能的排列)。已知存在唯一的非负整数 同时满足所有方程(在合适的排列下),求这个 。
解题思路
由于每个方程只有 种可能的排列方式,我们可以枚举第一条方程的所有排列,对每种排列解出 (若该排列对应方程 有整数解且 ),然后验证这个 是否满足其余所有方程(每条方程存在一种排列使得等式成立)。由于解唯一,最多验证 次即可找到答案。复杂度 。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<tuple<long long, long long, long long>> eq(n);
for (int i = 0; i < n; i++) {
long long u, v, w;
cin >> u >> v >> w;
eq[i] = {u, v, w};
}
auto [u0, v0, w0] = eq[0];
auto check = [&](long long x) -> bool {
for (auto [u, v, w] : eq) {
if (u * x + v == w) continue;
if (u * x + w == v) continue;
if (v * x + u == w) continue;
if (v * x + w == u) continue;
if (w * x + u == v) continue;
if (w * x + v == u) continue;
return false;
}
return true;
};
long long ans = -1;
if ((w0 - v0) % u0 == 0) {
long long x = (w0 - v0) / u0;
if (x >= 0 && check(x)) ans = x;
}
if ((v0 - w0) % u0 == 0) {
long long x = (v0 - w0) / u0;
if (x >= 0 && check(x)) ans = x;
}
if ((w0 - u0) % v0 == 0) {
long long x = (w0 - u0) / v0;
if (x >= 0 && check(x)) ans = x;
}
if ((u0 - w0) % v0 == 0) {
long long x = (u0 - w0) / v0;
if (x >= 0 && check(x)) ans = x;
}
if ((u0 - v0) % w0 == 0) {
long long x = (u0 - v0) / w0;
if (x >= 0 && check(x)) ans = x;
}
if ((v0 - u0) % w0 == 0) {
long long x = (v0 - u0) / w0;
if (x >= 0 && check(x)) ans = x;
}
cout << ans << '\n';
}
return 0;
}
T3 午枫的宝藏
题意简述
有 名水手(不包括船长),按顺位 到 继承。船长提出分配金币方案,全员(包括船长)投票。若半数及以上通过则执行,否则船长被处死,由第 顺位继承人接任并重新分配,以此类推。所有水手聪明、贪婪、互不信任,每个人在保证自己不被杀的前提下争取最大利益。求船长在保证自己存活的前提下分出去的最少金币总数,并按 输出,其中 是分配给第 顺位继承人的金币数。
解题思路
从少到多递推。记总人数为 (包括船长)。当只有船长一人()时,不需要分金币。对于 人情况,船长需要争取半数以上票(包括自己)。由于第 顺位继承人在下一轮会成为船长,他一定会反对当前船长的提案(因为反对后自己就能掌权)。因此船长只能拉拢后面的人。
通过归纳可以发现,最终分配方案为:第 顺位继承人得到 枚金币当且仅当 为偶数,否则得到 枚。即 当 为偶数, 当 为奇数。因此需要计算 ,共 项。该和为 。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
long long n;
cin >> n;
long long m = n / 2;
long long ans = m % MOD * ((m + 1) % MOD) % MOD;
cout << ans << '\n';
}
return 0;
}
T4 午枫的罗盘
题意简述
有 条刻度线 ,其中 为起始线, 由 逆时针旋转 得到。求有多少对 ()使得 。
解题思路
两条线垂直当且仅当它们的夹角为 的奇数倍。由于每次旋转 , 与 的夹角为 。 等价于 ,即 。因此 必须为偶数才有解,否则答案为 。
当 为偶数时,令 。条件化为 。由于 ,考虑将 按模 分组,每组内下标相差 的倍数。实际上, 与 垂直,与 也垂直,等等。统计所有满足 的对数即可。更简单的方法:将 条线按模 分成 组,每组有 或 条线。每组的贡献为该组内任取两条线的组合数?不对,垂直并不发生在同组内,而是发生在相隔 的倍数且差为奇数倍 的组之间?实际上 与 垂直当且仅当 是 的奇数倍。因此对于每组模 的同余类,它们内部相邻的线差恰好是 ,但垂直要求差为 。每个同余类内部,若按顺序排列,第 条线与第 条线垂直,与第 条线差 不垂直,与第 条线垂直,以此类推。因此每个同余类中,将线按顺序编号 ,则一对线 满足 为奇数即垂直。该同余类内的垂直对数为 。将所有同余类的贡献相加即可。
设 ,,则 个组有 条线, 个组有 条线。答案为:
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
long long n, k;
cin >> n >> k;
if (k % 2 == 1) {
cout << "0\n";
continue;
}
long long d = k / 2;
long long a = n / d;
long long b = n % d;
auto f = [](long long x) {
return (x / 2) * ((x + 1) / 2);
};
long long ans = b * f(a + 1) + (d - b) * f(a);
cout << ans << '\n';
}
return 0;
}
T5 午枫的航海日志
题意简述
给定长度为 的序列 ,求有多少种不同的正整数对 ,使得序列中存在一个子序列(不连续)恰好为 。
解题思路
我们需要找到所有满足条件的 。枚举 和 不可行,考虑枚举子序列中第一个 和第二个 的位置。设第一个 在位置 ,第二个 在位置 (),并且它们之间至少有一个 。同时,在 之后需要存在一个 ()。由于 可以是任意正整数,实际上只要 之后存在任意一个正整数()即可,因为 可以取那个正整数的值。但注意 必须与 独立,且 只计一次。
更直接的做法:对于每个 ,考虑它出现的所有位置。我们需要两个 的位置中间有 ,且第二个 之后有正整数。如果存在这样的两个 ,那么所有出现在第二个 之后的正整数都可以作为 。因此对每个 ,贡献的 的数量等于第二个 之后的不同正整数的个数。为了去重,我们应当对每个 统计它能产生的 的集合,最后累加不同 的数量。
由于 ,可以枚举 。对于每个 ,找到第一个 位于两个 之间的可行方案。设 的出现位置为 。如果存在 和 使得 且区间 内包含至少一个 ,那么 之后的所有正整数都可以作为 。因此我们只需要记录每个 的“最早”满足条件的第二个 的位置,然后统计该位置之后的不同正整数个数。可以用后缀预处理每个位置之后的不同正整数的数量(注意只统计正整数,不包括 )。最后对每个 累加即可。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int a[N], suf[N];
bool vis[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<int> pos[N];
for (int i = 1; i <= n; i++) {
if (a[i] > 0) pos[a[i]].push_back(i);
}
// 后缀中不同正整数的数量
int cnt = 0;
memset(vis, 0, sizeof(vis));
for (int i = n; i >= 1; i--) {
if (a[i] > 0 && !vis[a[i]]) {
vis[a[i]] = true;
cnt++;
}
suf[i] = cnt;
}
long long ans = 0;
for (int p = 1; p <= 1000000; p++) {
if (pos[p].size() < 2) continue;
int best = n + 1;
for (int t = 0; t < (int)pos[p].size() - 1; t++) {
int i = pos[p][t], j = pos[p][t + 1];
// 检查 i 和 j 之间是否有 0
bool ok = false;
for (int k = i + 1; k < j; k++) {
if (a[k] == 0) {
ok = true;
break;
}
}
if (ok) {
best = min(best, j);
}
}
if (best <= n) {
ans += suf[best + 1];
}
}
cout << ans << '\n';
}
return 0;
}
T6 午枫的密码本
题意简述
给定字符串 和一个极大的整数 ,将 重复拼接 次得到 ,求 的最长严格递增子序列(LIS)的长度。
解题思路
当 (即 )时,可以从不同重复中分别取每个字母,从而得到所有不同字母,答案为 中不同字母的种类数。
当 时,暴力构造 重复 次,长度不超过 ,直接 DP 求 LIS 即可。
由于 以字符串形式给出,若其长度 或数值 则按第一种情况处理,否则转整数后暴力。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
string s, ks;
cin >> s >> ks;
set<char> st(s.begin(), s.end());
int m = st.size();
if (ks.size() > 2 || (ks.size() == 2 && (ks[0] > '2' || (ks[0] == '2' && ks[1] > '5')))) {
cout << m << '\n';
continue;
}
int k = stoi(ks);
string t;
for (int i = 0; i < k; i++) t += s;
int n = t.size();
vector<int> dp(n, 1);
int ans = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (t[j] < t[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
ans = max(ans, dp[i]);
}
cout << ans << '\n';
}
return 0;
}
全部评论 2
这次怎么有两道纯数学T3和T4。
10小时前 来自 浙江
1T4纯数学,是6题之中最难的。
10小时前 来自 浙江
1























有帮助,赞一个