题解 | #小L的三角尺#
小L的三角尺
https://ac.nowcoder.com/acm/contest/120566/A
这是关于G,B题的个人想法
G|小L的散步
- 有 n 块连续排列的石块,每块石块有长度,将这些石块依次拼接后,石块之间的 “缝隙”(每块石块的右端点)会形成一个递增的位置序列(比如第 1 块长 2、第 2 块长 3,则缝隙位置为 2、5)。
- 有一个人从起点(位置 0)出发,分 m 步行走,每步走固定长度,最终形成 m+1 个 “脚掌位置”(起点 + 每步走完后的位置,比如走 2 步、每步走 1,则位置为 0、1、2)。
- 每个人的脚掌长度为 l,即站在位置 pos 时,脚掌覆盖区间是 [pos, pos+l)。
核心思路
1:预处理缝隙位置
- 遍历输入的 n 块石块长度,累加得到每块石块的右端点(缝隙)位置,并存储到有序数组 gaps 中(因为石块是连续拼接的,gaps 天然严格递增)。
2:预处理脚掌位置(O (m))
- 遍历输入的 m 步行走长度,累加得到每一步走完后的位置,加上起点 0,形成 m+1 个脚掌位置数组 steps
3:二分查找验证是否踩到缝隙
- 对每个脚掌位置 pos,做以下判断:
- 用 upper_bound(本质是二分查找)在有序的 gaps 中,找到第一个大于 pos 的缝隙位置 gap_pos(这是当前 pos 右侧最近的缝隙)。
- 核心判断条件:pos < gap_pos && pos + l > gap_pos
- pos < gap_pos:确保缝隙在脚掌的右侧(没被脚掌左端点超过);
- pos + l > gap_pos:确保缝隙在脚掌覆盖的区间内(没超过脚掌右端点);
- 两者同时满足 → 脚掌覆盖了该缝隙(踩到),直接判定为 YES 并终止检查。
代码展示
#include <bits/stdc++.h>
using namespace std;
int main() {
long long n, m, l;
cin >> n >> m >> l;
vector<long long> gaps;
long long total = 0;
for (int i = 0; i < n; ++i) {
long long x;
cin >> x;
total += x;
gaps.push_back(total);
}
vector<long long> steps(m + 1);
steps[0] = 0;
for (int i = 1; i <= m; ++i) {
long long y;
cin >> y;
steps[i] = steps[i - 1] + y;
}
bool stepped = false;
for (long long pos : steps) {
auto it = upper_bound(gaps.begin(), gaps.end(), pos);
if (it != gaps.end()) {
long long gap_pos = *it;
if (pos < gap_pos && pos + l > gap_pos) {
stepped = true;
break;
}
}
}
cout << (stepped ? "YES" : "NO") << endl;
return 0;
}
B|小L的彩球
- 组合数计算:利用费马小定理求阶乘的逆元,通过公式 C(n,m)=n!/m!(n−m)!mod998244353 计算组合数。
- 段数拆分:外露线数 t 对应彩球分盒的 “切换次数”,切换 t 次会形成 t+1 个连续段;将 t+1 个段拆分为 “左盒段数” 和 “右盒段数”(两种拆分方式)。
- 隔板法计数:将左盒的 x 个球分到左盒段数对应的段中,右盒的 n-x 个球分到右盒段数对应的段中,两种拆分方式的方案数之和即为答案。
代码展示
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10, mod = 998244353;
typedef long long LL;
LL f[N];
LL qmi(LL x, LL k)
{
LL t = x, res = 1;
while (k)
{
if (k & 1) res = res * t % mod;
t = t * t % mod;
k >>= 1;
}
return res;
}
LL c(int n, int m)
{
if (m > n) return 0;
if (m == 0) return 1;
return f[n] * qmi(f[n - m], mod - 2) % mod * qmi(f[m], mod - 2) % mod;
}
void solve()
{
int n, x, t;
scanf("%d%d%d", &n, &x, &t);
if (x == n || t == 0)
{
printf("%d\n", x == n && t == 0);
return ;
}
int num[2] = {t + 1 >> 1, t + 1 - (t + 1 >> 1)};
LL ans = 0;
for (int i = 0; i < 2; i ++)
ans = (ans + c(x - 1, num[i] - 1) * c(n - x - 1, t + 1 - num[i] - 1)) % mod;
printf("%lld\n", ans);
}
int main()
{
f[0] = 1;
for (int i = 1; i < N; i ++) f[i] = f[i - 1] * i % mod;
int t = 1;
scanf("%d", &t);
while (t --) solve();
return 0;
}
查看2道真题和解析