2025春训第九场

A. 鲁的智力
我刚开始看到这道题之后默默的放弃了,选择了后面的 dp 题。
最小排名:至少有多少个人比他分数高。
最大排名:至少有多少个人比他分数低。
两边都是考虑极限情况,以第一种为例。一个人要想稳定的分数比另一个人高,那必然每道题都比另一个高,于是问题转化成至少有几个人每道题分数都比他高。设第 i 道题的排名是 \(a_i\),考虑一道题的时候,情况是确定的,这道题比他高的人肯定满足所有题都比他高;两道题的时候,考虑边界情况——第二道题比他高的人和第一道题比他高的人最大程度的不重叠,所以人数应该是 \(\max\{0, a_2 + a_1 - m - 1\}\);现在相当于前两道题已经合成一道大的题了,再不断往下计算就能算出来最后的结果。第二种同理。
#include <iostream>
using namespace std;
int a[1010];
int main() {
int n, m, l, r;
cin >> n >> m;
l = r = m - 1;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
l = max(0, a[i] + l - m);
r = max(0, (m - a[i]) + r - (m - 1));
}
cout << l + 1 << endl << m - r << endl;
return 0;
}
B. 鲁的女孩
最优的配对方案是一个正序一个倒序一一配对。关键的性质是 1 ≤ a, b ≤ 100,开两个数组记录 [1, 100] 每个数的个数,然后双指针扫描一次即可得出答案。事实可以证明,挺容易写挂的(比如我)
#include <iostream>
#include <algorithm>
using namespace std;
int a[210], b[210];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int ta, tb;
cin >> ta >> tb;
a[ta]++, b[tb]++;
int mx = -0x3f3f3f3f;
int l = 0, r = 101;
int lc = 0, rc = 0;
while (l <= 100 && r > 0) {
if (lc && rc) {
mx = max(mx, l + r);
int tmp = min(lc, rc);
lc -= tmp, rc -= tmp;
}
if (!rc) rc = b[--r];
if (!lc) lc = a[++l];
}
cout << mx << endl;
}
return 0;
}
C. 鲁的石板
前排警告,最好别用 vector 一直申请内存容易 TLE😭
这题好像有数学的方法可以一个式子秒了,没关系,我会一个矩阵快速幂秒了。
线性 dp 的方法是开一个状态数组 f[N][2],f[i][0] = 表示涂到第 i 块且第 i 块颜色和第一块一致的方案数,f[i][1] 表示涂到第 i 块且第 i 块颜色和第一块不一致的方案数,答案是 f[n][1]。
\[f_{i, 0} = f_{i - 1, 1}\]
\[f_{i, 1} = (m - 1)f_{i - 1, 0} + (m - 2)f_{i - 1, 1}\]
用矩阵乘法写这个递推式
\[\begin{pmatrix} f_{i - 1, 0} & f_{i - 1, 1} \end{pmatrix} \begin{pmatrix} 0 & m - 1\\ 1 & m - 2 \end{pmatrix} = \begin{pmatrix} f_{i, 0} & f_{i, 1} \end{pmatrix}\]
显然答案是 \([\begin{pmatrix} m & 0\end{pmatrix}\begin{pmatrix} 0 & m - 1 \\ 1 & m - 2 \end{pmatrix}^{n - 1}]_{1, 2}\),即 \(m\begin{pmatrix} 0 & m - 1 \\ 1 & m - 2 \end{pmatrix}^{n - 1}_{1, 2}\).
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1000000007;
long long mat[2][2], res[2][2], a[2][2], b[2][2];
void mul(long long a[][2], long long b[][2]) {
static long long t[2][2];
t[0][1] = t[0][0] = t[1][1] = t[1][0] = 0;
for (int k = 0; k < 2; ++k) {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
t[i][j] = (t[i][j] + a[i][k] * b[k][j] % MOD) % MOD;
}
}
}
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
a[i][j] = t[i][j];
}
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
if (n == 1) {
cout << m << endl;
continue;
}
mat[0][0] = 0, mat[0][1] = m - 1, mat[1][0] = 1, mat[1][1] = m - 2;
res[0][0] = res[1][1] = 1, res[0][1] = res[1][0] = 0;
n--;
while (n) {
if (n & 1) mul(res, mat);
mul(mat, mat);
n >>= 1;
}
printf("%d\n", (long long)m * res[0][1] % MOD);
}
return 0;
}
D. 游览计划
提供一个暴力的解法,具体有多暴力见下图,差点拼尽全力无法战胜了……
暴力的流程是
对于每一个点跑一遍 bfs 找到到所有其他点的最短路;
对于每一个点找到最短路最长的三个点,记下来;
暴力枚举中间点 B, C,枚举到 B 距离最长的三个 A,到 C 距离最长的三个 D,过滤所有A, B, C, D 重复情况,把路径加起来更新答案。
这个做法显然是正确的,存了三个最远的点保证了最坏的情况下也不至于全部冲突。
- 代码后续更新了一下,不会 997ms 了。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 4010, M = 5010;
int head[N], ne[M * 2], ver[M * 2], tot;
int d[N][N], q[N], hh, tt;
int mxp[N][3];
inline void read(int &_) {
_ = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) _ = _ * 10 + c - 48, c = getchar();
}
inline void add(int x, int y) {
ver[++tot] = y;
ne[tot] = head[x];
head[x] = tot;
}
int main() {
int n, m;
read(n), read(m);
for (int i = 1; i <= m; ++i) {
int x, y;
read(x), read(y);
add(x, y);
add(y, x);
}
memset(d, -1, sizeof(d));
for (int i = 1; i <= n; ++i) {
hh = tt = 0;
d[i][i] = 0;
q[tt] = i;
while (hh <= tt) {
int x = q[hh++];
if (d[i][x] >= d[i][mxp[i][0]]) {
mxp[i][2] = mxp[i][1], mxp[i][1] = mxp[i][0];
mxp[i][0] = x;
}
else if (d[i][x] >= d[i][mxp[i][1]]) {
mxp[i][2] = mxp[i][1];
mxp[i][1] = x;
}
else if (d[i][x] >= d[i][mxp[i][2]]) {
mxp[i][2] = x;
}
for (int j = head[x]; j; j = ne[j]) {
int y = ver[j];
if (~d[i][y]) continue;
d[i][y] = d[i][x] + 1;
q[++tt] = y;
}
}
}
int res = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
for (int k = 0; k < 3; ++k) {
if (mxp[i][k] == j) continue;
for (int p = 0; p < 3; ++p) {
if (i != mxp[j][p] && mxp[i][k] != mxp[j][p])
res = max(res, d[mxp[i][k]][i] + d[i][j] + d[j][mxp[j][p]]);
}
}
}
}
printf("%d\n", res);
return 0;
}
Subscribe to my newsletter
Read articles from Star directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Star
Star
A Chinese OIer and a tosser.