A – Table Tennis Training
思博题,考虑两端和奇偶性即可。
// A.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll n, A, B, ans = 0x7fffffffffffffff;
scanf("%lld%lld%lld", &n, &A, &B);
ans = min(A - 1, n - B) + 1 + ((B - A - 1) >> 1);
if (!((A^B) & 1))
ans = min(ans, (B - A) >> 1);
printf("%lld\n", ans);
return 0;
}
B – Voting Judges
首先要意识到这个问题是可二分的,因为如果答案更大可行、那么小答案也可行。接下来考虑写一个 check。首先要判掉单独加上都无法入围的情况,然后算比 \(a_{mid}\) 小的那些 \(a_i\) 可以提供的票数。最后算这个票数是否能使之入围即可。
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200;
int n, m, V, P, ai[MAX_N];
bool check(int mid)
{
if (ai[mid] + m < ai[P])
return false;
long long acc = 0;
for (int i = P; i <= n; i++)
if (i != mid && ai[i] < ai[mid] + m)
acc += min(m, ai[mid] + m - ai[i]);
return acc >= 1LL * (V - 1) * m;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &V, &P);
for (int i = 1; i <= n; i++)
scanf("%d", &ai[i]);
sort(ai + 1, ai + 1 + n), reverse(ai + 1, ai + 1 + n);
V = max(1, V - P + 1);
int l = P, r = n, res = P;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid))
res = mid, l = mid + 1;
else
r = mid - 1;
}
printf("%d\n", res);
return 0;
}
C – Domino Quality
首先可以找出 \(3\) 的、单元数为 \(2\) 的那个情况,这个情况直接输出即可;另外,我们能自己搞出来 \(4 \to 7\) 的、单元数为 \(3\) 的情况,之后如果 \(n > 7\),则表示成 \(n = 4x + y, y > 3\),然后拼在对角线上即可。
// C.cpp
#include <bits/stdc++.h>
using namespace std;
int n;
char buffer[1010][1010];
const char m3[3][4] =
{"aa.",
"..a",
"..a"};
const char m4[4][5] =
{"aabc",
"ddbc",
"bcaa",
"bcdd"};
const char m5[5][6] =
{"aabba",
"bcc.a",
"b..cb",
"a..cb",
"abbaa"};
const char m6[6][7] =
{"aabc..",
"ddbc..",
"..aabc",
"..ddbc",
"bc..aa",
"bc..dd"};
const char m7[7][8] =
{"aabbcc.",
"dd.dd.a",
"..d..da",
"..d..db",
"dd.dd.b",
"..d..dc",
"..d..dc"};
void writeBuffer(int id, int x, int y)
{
for (int i = 0; i < id; i++)
{
const char *str;
switch (id)
{
case 3:
str = m3[i];
break;
case 4:
str = m4[i];
break;
case 5:
str = m5[i];
break;
case 6:
str = m6[i];
break;
case 7:
str = m7[i];
break;
}
for (int j = 0; str[j]; j++)
buffer[x + i][y + j] = str[j];
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
buffer[i][j] = '.';
if (n <= 2)
puts("-1"), exit(0);
if (n <= 7)
writeBuffer(n, 0, 0);
else
{
int base = 0;
writeBuffer(n % 4 + 4, base, base), base += n % 4 + 4;
while (base < n)
writeBuffer(4, base, base), base += 4;
}
for (int i = 0; i < n; i++)
printf("%s\n", buffer[i]);
return 0;
}
D – Problem Scores
这应该是本次最有意思的题目了,之前 lcx 的某个教练群里发过。
其实就是要满足其 \(k + 1\) 前缀和严格大于其 \(k\) 后缀和,写出来即:
\[ \sum_{i = 1}^{k + 1} a_i > \sum_{i = n – k + 1}^n a_i \]
一共会有 \(n\) 个这样的限制条件,我们尝试寻找性质来缩减之。首先,对于 \(k > \lfloor \frac{n}{2} \rfloor\) 的情况,存在前缀和后缀中间重叠的部分,这个可以直接移项消掉,转化成 \(k \leq \lfloor \frac{n}{2} \rfloor\) 的情况。
再考虑利用 \(a_i \leq a_{i + 1}\) 的性质,则发现当 \(k = \lfloor \frac{n}{2} \rfloor\) 时满足,\(k \leq \lfloor \frac{n}{2} \rfloor\) 的情况可以被充分满足。
那么我们现在只需要保证 \(\sum_{i = 1}^{k + 1} a_i > \sum_{i = n – k + 1}^n a_i, k = \lfloor \frac{n}{2} \rfloor\) 即可。
现在我们对这个序列进行差分,使得 \(a_k = 1 + \sum_{i = 1}^k \Delta_i\)。首先显然有 \(\Delta \geq 0\)。其次,带回式子时,发现前缀和可以被拆解为 \(\sum \Delta_i c_i\),其中这个 \(c_i\) 很好算。考虑带入:
\[ \sum_{i = 1}^{k + 1} 1 + \sum_{j = 1}^i \Delta_j > \sum_{i = n – k + 1}^n 1 + \sum_{j = 1}^i \Delta_j \\ 1 + \sum_{i = 1}^{k + 1} \sum_{j = 1}^i \Delta_j > \sum_{i = n – k + 1}^n \sum_{j = 1}^i \Delta_j \\ \sum_{i = 1}^{k + 1} \sum_{j = 1}^i \Delta_j \geq \sum_{i = n – k + 1}^n \sum_{j = 1}^i \Delta_j \\ \sum_{j = 1}^n \Delta_j \sum_{i = 1}^{k + 1} [j \leq i] \geq \sum_{j = 1}^n \Delta_j \sum_{i = n – k + 1}^n [j \leq i] \\ \sum_{j = 1}^{k + 1} \Delta_j (k + 2 – j) \geq \sum_{j = 1}^n \Delta_j (n – \max(j, n – k + 1) + 1) \]
得到:
\[ (k + 1) \Delta_1 + \sum_{j = 2}^{k + 1} \Delta_j (k + 2 – j) \geq k\Delta_1 + \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) – \sum_{j = 2}^{k + 1} \Delta_j (k + 2 – j) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) – \sum_{j = 2}^n \Delta_j \max(0, k + 2 – j) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 – (\max(j, n – k + 1) + \max(0, k + 2 – j))) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 – \max(j, k + 2, n – k + 1, n + 3 – j)) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 + \min(-j, – k – 2, – n + k – 1, – n – 3 + j)) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j \min(n – j + 1, n – k – 1, k, j – 2) \\ \]
提醒一下,\(k = \lfloor \frac{n}{2} \rfloor\):

区间是离散的,所以最后 \(\min(n – j + 1, n – k – 1, k, j – 2) = \min(j – 2, n – j + 1)\)。最后整个不等式形如:
\[ \Delta_1 \geq [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [0, 1, 2, 3, \dots 3, 2, 1] \]
考虑设:
\[ a = \Delta_2 + \Delta_3 + \Delta_4 + \dots + \Delta_n \\ b = [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [0, 1, 2, 3, \dots 3, 2, 1] \]
发现 \(\Delta_1 \leq n – 1 – a, \Delta_1 \geq b \),那么,\(\Delta_1\) 的选择方式有 \(\max(0, n – 1 – (a + b))\) 种。\(a + b = [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [1, 2, 3, 4, \dots 4, 3, 2]\),所以我们用 \(DP\) 算下和为 \(j = a + b\) 的 \(\Delta_i\) 序列的方案数,然后用方案数乘上一个 \(\max(0, n – 1 – (a + b))\) 即为答案。
// D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 5050;
int n, mod, dp[MAX_N], coeff[MAX_N];
int main()
{
scanf("%d%d", &n, &mod), dp[0] = 1;
for (int i = 1; i < n; i++)
for (int j = min(n - i + 1, i), k = j; j < n; j++)
dp[j] = (0LL + dp[j] + dp[j - k]) % mod;
int ans = 0;
for (int i = 0; i < n; i++)
ans = (0LL + ans + 1LL * dp[i] * (n - i) % mod) % mod;
printf("%d\n", ans);
return 0;
}