一、比赛基本情况
- 比赛日期:2026-08-11
- 比赛时长:4h
- 比赛得分:2pts
二、比赛整体回顾
T1 ~ T2 很快就完成了,还是比较简单。
T3 是搜索题目,一开始就想正解去了,没写暴力;导致考试时没拿到部分分;如果没有一次性想到正解一定要先暴力保分,后面还可以用作对拍。
T4 是关于逆序对的题目,想出了题目和逆序对之间的关系但实现的时候没有注意细节,以后写题目时要想清楚了再写。
T5 是关于模拟的题目,考试时想到了枚举出现了几个字符,然后判断,但因为一些未定义行为导致没通过样例。
T6 因为前面去死磕了,所以每留出太多时间来看这道题,下次一定要分配好时间。
三、比赛理想时间安排
- T1 ~ T2 在 30 min 内完成
- T3 花上十分钟理清代码逻辑,大概在 30min 完成本题
- T4 很容易想到逆序对,大概花 30min 理清思路,大概 15min 写代码
- T5 也很容易想到枚举,细节较多,大约花 15min 写好伪代码再写代码,大约用时 40min
- T6 稍微想一想能知道要记录两维状态,细节也比较多,大概 60min
四、题目复盘
Following Directions S
初始思路
对每个点进行搜索,找到没有修改的答案,然后修改就是改动一个位置,但这样是不对的,最后一个需要改动前面的。所以思路就变成往前面跳,复杂度最多是 \(O(n)\),理论上不写错可以过了这一题。
错误原因
由于每个点有两种情况,从上面来、从左边来。要分别考虑而不是单独考虑一种情况,所以导致只有 6pts。
赛后题解
每次向上跳,理论上时间复杂度是 \(O(n^2)\) 的,但是随机数据跑的飞快。
递归的常数是很大的,尽量减少递归次数。
本题总结
本题暴露出调试能力的缺陷调试时不能使用瞪眼法来盯着黑窗口,要动手在草稿纸上画一画。
补题代码
#include <bits/stdc++.h>using namespace std;#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1500 + 7;
constexpr int P = 998244353;int n, q, a[N][N], vis[N][N], bel[N * N], tot;
ll cost[N][N], ans;
char s[N][N];
bool b[N][N];int dfs(int x, int y) {if (x > n) return a[x][y];if (y > n) return a[x][y];vis[x][y] = tot;if (s[x][y] == 'R') return dfs(x, y + 1);else return dfs(x + 1, y);
}void modify(int x, int y, int k) {if (!x || !y) return;if (b[x][y]) return;b[x][y] = true;cost[x][y] = k;if (s[x][y - 1] == 'R') modify(x, y - 1, k);if (x == 1 || s[x - 1][y] == 'D') {modify(x - 1, y, k);}
}void update(int x, int y, int sgn) {if (!x || !y) return;ans += cost[x][y] * sgn;if (s[x][y - 1] == 'R') update(x, y - 1, sgn);if (x == 1 || s[x - 1][y] == 'D') update(x - 1, y, sgn);
}int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%s", s[i] + 1);scanf("%d", &a[i][n + 1]);}for (int i = 1; i <= n; i++) scanf("%d", &a[n + 1][i]);for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (!vis[i][j]) {++tot;bel[tot] = dfs(i, j);}}}for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {cost[i][j] = bel[vis[i][j]];ans += cost[i][j];}cost[i][n + 1] = a[i][n + 1];}for (int i = 1; i <= n; i++) {cost[n + 1][i] = a[n + 1][i];}printf("%lld\n", ans);scanf("%d", &q);while (q--) {int x, y;scanf("%d%d", &x, &y);if (s[x][y] == 'R') {s[x][y] = 'D';update(x, y, -1);modify(x, y, cost[x + 1][y]);update(x, y, 1);} else {assert(s[x][y] == 'D');s[x][y] = 'R';update(x, y, -1);modify(x, y, cost[x][y + 1]);update(x, y, 1);}printf("%lld\n", ans);}return 0;
}
Sorting Color Balls
初始思路
由于题目所给过程就是冒泡排序的过程,很容易想到逆序对,相同颜色的逆序对个数肯定是互补干扰的,所以可以用总的逆序对减去相同颜色的逆序对总和。
错误原因
实现上一些细节出现了问题。
赛后题解
就是用总体的减去相同的,实现要把颜色分开做归并排序。
补题代码
#include <bits/stdc++.h>using namespace std;#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 3e5 + 7;
constexpr int P = 998244353;int n;
int a[N], c[N], d[N];
vector<int> b[N];ll divide(int l, int r, int lx) {if (l == r) return 0;int mid = (l + r) / 2;ll res = divide(l, mid, lx) + divide(mid + 1, r, lx);int i = l, j = mid + 1, k = l;while (i <= mid && j <= r) {if (b[lx][i] <= b[lx][j]) d[k++] = b[lx][i++], res += j - mid - 1; else d[k++] = b[lx][j++];}while (i <= mid) d[k++] = b[lx][i++], res += j - mid - 1;while (j <= r) d[k++] = b[lx][j++];for (int p = l; p <= r; p++) b[lx][p] = d[p];return res;
}int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%d", &a[i]);for (int i = 1; i <= n; i++) scanf("%d", &c[i]), b[0].push_back(c[i]); for (int i = 1; i <= n; i++) {b[a[i]].push_back(c[i]);}ll res = divide(0, n - 1, 0);for (int i = 1; i <= n; i++) {int l = 0, r = (int)b[i].size() - 1;if (l > r) continue;res -= divide1(l, r, i);}printf("%lld\n", res);return 0;
}
本题总结
本题有一个技巧,就是冒泡排序的交换次数就是逆序对的个数。
Equal Frequencies
初始思路
由于 \(k\) 大于 \(26\) 时,字符集会不够用,所以可以枚举选了多少个字符;
错误原因
由于 UB,导致输入输入了一坨,输出是个不知道什么东西,好像是因为数组越界了、
赛后题解
枚举选了多少个字符,显然一定是 \(n\) 的因数,则个数一定是 \(\dfrac nk\),找到最小化的 \(k\) 之后构造数列,如果可行直接删掉一个,如果不行,就找到剩余的。
补题代码
#include <bits/stdc++.h>using namespace std;
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e5 + 7;
constexpr int P = 998244353;int n;
char s[N];void solve() {scanf("%d", &n);scanf("%s", s + 1);array<int, 26> cnt{};array<array<int, 2>, 26> cset{};for (int i = 1; i <= n; i++) ++cnt[s[i] - 'a'];for (int i = 0; i < 26; i++) cset[i] = {cnt[i], i};sort(all(cset), greater<array<int, 2>>());int minval = inf, minid = -1;for (int k = 1; k <= 26; k++) {if (n % k != 0) continue;int now = n / k;ll res = 0;for (int i = 0; i < k; i++) res += abs(now - cset[i][0]);for (int i = k; i < 26; i++) {assert(i < (int)cset.size());res += cset[i][0];}res /= 2;if (res < minval) minval = res, minid = k;}printf("%d\n", minval);for (int i = 0; i < 26; i++) cnt[i] = 0;for (int i = 0; i < minid; i++) cnt[cset[i][1]] = n / minid;for (int i = 1; i <= n; i++) {int found = -1;for (int j = 0; j < minid; j++) if (s[i] == (cset[j][1] + 'a')) {found = cset[j][1];break;} if (found < 0) {s[i] = '\0';} else if (!cnt[found]) {s[i] = '\0';} else {--cnt[found];}}for (int i = 1; i <= n; i++) {if (s[i] != '\0') continue;for (int j = 0; j < minid; j++) {if (cnt[cset[j][1]]) {--cnt[cset[j][1]];s[i] = cset[j][1] + 'a';break;}}}for (int i = 1; i <= n; i++) printf("%c", s[i]);puts("");
}int main() {int test;scanf("%d", &test);for (; test--; ) solve();return 0;
}
本题总结
本题的核心在于几个点
- 枚举长度,找到可能的最小值;(最小的操作次数)
- 判断是否保留
- 构造数列
Two Currencies
初始思路
NULL
错误原因
NULL
赛后题解
由于当 \(s>50\times50\) 时,就不需要更多银币了,所以其实只要考虑到 \(2500\),并不是 \(250\)。
所以可以跑分层图上跑最短路。
具体来说建图的时候要建两种图,一种是边与边的,一种是点的。
补题代码
#include <bits/stdc++.h>using namespace std;
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
typedef array<ll, 3> arr;
constexpr int N = 50 + 7, V = 2600 + 7;
constexpr int P = 998244353;int n, m, s;
vector<arr> adj[N][V];
ll dis[N][V];
bool vis[N][V];void dijkstra(int sx, int sy) {priority_queue<arr, vector<arr>, greater<arr>> q;memset(dis, 0x3f, sizeof(dis));memset(vis, false, sizeof(vis));dis[sx][sy] = 0;q.push({dis[sx][sy], sx, sy});while (!q.empty()) {auto [d, u, cost] = q.top(); q.pop();if (vis[u][cost]) continue;vis[u][cost] = true;for (auto [v, pay, t] : adj[u][cost]) {if (dis[u][cost] + t < dis[v][pay]) {+dis[v][pay] = dis[u][cost] + t;q.push({dis[v][pay], v, pay});}} }
}int main() {scanf("%d%d%d", &n, &m, &s); s = min(s, 2450);for (int i = 1; i <= m; i++) {int u, v, a, b;scanf("%d%d%d%d", &u, &v, &a, &b);for (int j = a; j <= 2450; j++) {adj[u][j].push_back({v, j - a, b});adj[v][j].push_back({u, j - a, b});}}for (int i = 1; i <= n; i++) {int c, d;scanf("%d%d", &c, &d);for (int j = c; j <= 2450; j++) {adj[i][j - c].push_back({i, j, d});}}dijkstra(1, s);for (int i = 2; i <= n; i++) {ll ans = lnf;for (int j = 0; j <= 2450; j++) ans = min(ans, dis[i][j]);printf("%lld\n", ans);}return 0;
}
本题总结
本题的核心在于几个点
- 想到建分层图
总结
这次大部分题目都是实现问题。
下次考试要注意几个点:
- 合理分配时间
- 每道题目如果没有把握一次想出,先打好暴力
- 写代码之前先想好要写什么