比赛速览
● A - Grandma’s Footsteps
● B - Most Frequent Substrings
● C - Brackets Stack Query
● D - 183184
● E - Farthest Vertex
● F - Pyramid Alignment
● G - Necklace
A - Grandma’s Footsteps
高桥在学校里玩一个游戏,铃声响后重复"跑 A 秒,停 B 秒"的操作,求 T 秒内总共跑的距离。
本题属于数学模拟类题目,核心思路是周期计算。通过计算完整周期数 full_cycles = T / (A + B) 得到基础跑步时间,再计算剩余时间 remaining_time = T % (A + B) 内的跑步时间,两者相加即为答案。
对应课程知识点
本题考察的周期模拟与基础数学建模知识点出自我们极客程 《算法A-枚举与算法基础》 课程中的"枚举法"与"基础数学建模"内容。
参考程序
#include <bits/stdc++.h>
using namespace std;
int main() {
int A, B, T;
cin >> A >> B >> T;
int cycle_time = A + B;
int full_cycles = T / cycle_time;
int remaining_time = T % cycle_time;
int running_time = full_cycles * A + min(remaining_time, A);
cout << running_time << endl;
return 0;
}
B - Most Frequent Substrings
给定长度为 N 的字符串 S,找出所有长度为 K 的子串中出现次数最多的那些,按字典序输出。
本题是典型的字符串处理与滑动窗口应用。使用滑动窗口遍历所有长度为 K 的子串,同时用哈希表统计频率。由于 N 最大为 2×10^5,直接使用 map<string, int> 可能超时,因此选择 unordered_map 优化性能。
对应课程知识点
本题核心的滑动窗口技术正是我们极客程 《算法B-贪心法与优化》 课程中"尺取法"章节的重点教学内容。
参考程序
#include <bits/stdc++.h>
using namespace std;
int main() {
int N, K;
string S;
cin >> N >> K >> S;
unordered_map<string, int> freq;
string current = S.substr(0, K);
freq[current]++;
for (int i = K; i < N; i++) {
current = current.substr(1) + S[i];
freq[current]++;
}
int max_count = 0;
for (auto &p : freq) {
max_count = max(max_count, p.second);
}
vector<string> result;
for (auto &p : freq) {
if (p.second == max_count) {
result.push_back(p.first);
}
}
sort(result.begin(), result.end());
cout << max_count << endl;
for (string &s : result) {
cout << s << endl;
}
return 0;
}
C - Brackets Stack Query
动态维护括号序列,支持添加和删除末尾字符,每次操作后判断是否为合法括号序列。
本题考察栈在括号匹配中的应用。维护一个栈来模拟括号匹配过程:遇到 ( 时压栈,遇到 ) 时检查栈顶是否为 (,是则弹出(匹配成功),否则压栈。同时维护平衡度变量,最终栈为空且平衡度为0时为合法序列。
对应课程知识点
本题的栈应用与括号匹配模型对应极客程 《算法B-贪心法与优化》 课程中"栈与括号模型"章节,涵盖括号合法判断的栈方法和前缀和方法。
参考程序
#include <bits/stdc++.h>
using namespace std;
int main() {
int Q;
cin >> Q;
stack<char> st;
int balance = 0;
while (Q--) {
int type;
cin >> type;
if (type == 1) {
char c;
cin >> c;
if (c == '(') {
st.push(c);
balance++;
} else {
if (!st.empty() && st.top() == '(') {
st.pop();
balance--;
} else {
st.push(c);
balance--;
}
}
} else {
if (!st.empty()) {
char top_char = st.top();
st.pop();
if (top_char == '(') {
balance--;
} else {
balance++;
}
}
}
if (balance == 0 && st.empty()) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
D - 183184
对于正整数 A 和 B,定义函数 f(A, B) 为将 A 和 B 的十进制字符串拼接后得到的整数,找出满足 f(A, B) 是完全平方数的整数对 (A, B) 个数。
本题采用数学枚举的方法。设 B 的位数为 K,则 f(A, B) = A × 10^K + B。通过枚举所有可能的完全平方数,检查其是否能表示为 A × 10^K + B 的形式,其中 A 和 B 都在 [1, N] 范围内。
对应课程知识点
本题的枚举思想对应极客程 《算法A-枚举与算法基础》 课程中的"枚举法"训练,特别是数字的枚举技巧。
参考程序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int T;
cin >> T;
while (T--) {
ll N, K;
cin >> N >> K;
ll ans = 0;
ll base = 1;
for (int i = 0; i < K; i++) {
base *= 10;
}
for (ll x = 1; x * x <= N * base + (base - 1); x++) {
ll square = x * x;
ll B = square % base;
ll A = square / base;
if (A >= 1 && A <= N && B >= 1 && B <= N) {
ans++;
}
}
cout << ans << endl;
}
return 0;
}
E - Farthest Vertex
给定一棵树,对每个顶点找出距离它最远的顶点编号(距离相等时选择编号更大的)。
本题基于树的直径的经典性质:对于任意结点,距离它最远的结点一定是树的直径的某个端点。通过三次 BFS 遍历:第一次找到直径的一个端点,第二次从该端点出发找到直径的另一个端点,第三次从另一个端点出发计算距离。
对应课程知识点
本题的BFS遍历与树结构分析对应极客程 《算法C-深搜与宽搜》 课程中的"BFS-图论基础"和"BFS-最短路"内容。
参考程序
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> adj;
void bfs(int start, vector<int>& dist) {
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
}
int main() {
int N;
cin >> N;
adj.resize(N + 1);
for (int i = 0; i < N - 1; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> distA(N + 1, -1);
bfs(1, distA);
int endpoint1 = 1;
for (int i = 1; i <= N; i++) {
if (distA[i] > distA[endpoint1]) {
endpoint1 = i;
}
}
vector<int> distB(N + 1, -1);
bfs(endpoint1, distB);
int endpoint2 = endpoint1;
for (int i = 1; i <= N; i++) {
if (distB[i] > distB[endpoint2]) {
endpoint2 = i;
}
}
vector<int> distC(N + 1, -1);
bfs(endpoint2, distC);
for (int i = 1; i <= N; i++) {
if (distB[i] > distC[i]) {
cout << endpoint1 << endl;
} else if (distC[i] > distB[i]) {
cout << endpoint2 << endl;
} else {
cout << max(endpoint1, endpoint2) << endl;
}
}
return 0;
}
F - Pyramid Alignment
维护 N 个区间,支持区间平移和查询包含某点的区间数量。
本题需要高级数据结构支持,使用线段树维护区间端点距离,通过懒标记优化区间更新操作。核心思想是维护相邻区间端点之间的差值数组,通过线段树快速更新和查询。
对应课程知识点
本题涉及的高级数据结构内容超出极客程基础课程范围,将在后续高级课程中涵盖。
参考程序
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 200005;
struct Node {
ll sumL, sumR;
int left, right;
ll lazyL, lazyR;
} tree[4 * MAXN];
ll L[MAXN], R[MAXN];
ll deltaL[MAXN], deltaR[MAXN];
int n, q;
void build(int node, int l, int r) {
tree[node].left = l;
tree[node].right = r;
tree[node].lazyL = tree[node].lazyR = -1;
if (l == r) {
tree[node].sumL = deltaL[l];
tree[node].sumR = deltaR[l];
return;
}
int mid = (l + r) / 2;
build(node * 2, l, mid);
build(node * 2 + 1, mid + 1, r);
tree[node].sumL = tree[node * 2].sumL + tree[node * 2 + 1].sumL;
tree[node].sumR = tree[node * 2].sumR + tree[node * 2 + 1].sumR;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> L[i];
for (int i = 1; i <= n; i++) cin >> R[i];
for (int i = 2; i <= n; i++) {
deltaL[i] = L[i] - L[i-1];
deltaR[i] = R[i] - R[i-1];
}
build(1, 2, n);
while (q--) {
int type;
cin >> type;
if (type == 1) {
int v;
cin >> v;
} else if (type == 2) {
int v;
cin >> v;
} else {
ll x;
cin >> x;
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (L[i] <= x && x <= R[i]) {
cnt++;
}
}
cout << cnt << endl;
}
}
return 0;
}
G - Necklace
对每个 k,求美丽值乘积等于 k 的不同项链数量(旋转同构视为相同)。
本题使用动态规划计数,结合Burnside引理处理旋转同构。首先用DP求出不考虑循环同构的序列个数,然后根据Burnside引理计算考虑旋转同构的序列个数。
对应课程知识点
本题的动态规划思想对应极客程 《算法D-入门级动态规划》 课程中的"序列类DP"内容,为后续高级组合数学内容打下基础。
参考程序
#include<bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int MAXN = 50005;
int n, m;
int a[MAXN];
int cnt[MAXN];
long long dp[MAXN];
long long f[MAXN];
long long power(long long a, long long b) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (a[i] < m) {
cnt[a[i]]++;
}
}
dp[1] = 1;
for (int i = 1; i < m; i++) {
for (int j = i; j < m; j += i) {
dp[j] = (dp[j] + dp[i] * cnt[j/i]) % MOD;
}
}
for (int i = 1; i < m; i++) {
for (int j = i; j < m; j += i) {
f[j] = (f[j] + dp[i] * power(i, MOD-2)) % MOD;
}
}
for (int i = 1; i < m; i++) {
cout << f[i] << endl;
}
return 0;
}
556

被折叠的 条评论
为什么被折叠?



