UVa 10799 OOPS! They did it Again...
本文探讨了外星人选举中装饰摩天大楼的方案数问题。给定编号从low到high的n座建筑,需选择k座构成等差数列且编号和能被k整除。通过数学建模,将问题转化为等差数列约束条件:当k=2时使用公式直接计算;k>2时枚举公差d并验证条件。算法对k=2实现O(1)复杂度,k>2最坏O(10^7)但实际高效。关键点在于正确推导数学条件并处理边界情况,展示了数学分析对算法优化的重要性。
题目描述
来自 Muzambaro\texttt{Muzambaro}Muzambaro 星球的外星人再次选举了不受欢迎的领导人。他们计划用灯光装饰城市中的 kkk 座摩天大楼。这些大楼位于 “The Deaf Street\texttt{The Deaf Street}The Deaf Street” 的一侧,共有 n=high−low+1n = high - low + 1n=high−low+1 座建筑,编号从 lowlowlow 到 highhighhigh ,相邻建筑距离相等。
需要选择 kkk 座建筑满足:
- 被选中的建筑编号构成等差数列(即等间距)
- 这 kkk 个编号的和能被 kkk 整除
给定 lowlowlow 、 highhighhigh 和 kkk ,求满足条件的方案数。
输入格式:多组数据,每行三个整数 lowlowlow , highhighhigh , kkk ,以 0 0 0 结束。
输出格式:对于每组数据,输出 Case i: ans ,其中 iii 为测试序号, ansansans 为答案。
题目分析
数学模型
设选择的建筑编号为等差数列: a,a+d,a+2d,…,a+(k−1)da, a+d, a+2d, \dots, a+(k-1)da,a+d,a+2d,…,a+(k−1)d ,其中:
- aaa 为首项(首座建筑的编号)
- ddd 为公差(建筑间距)
- 所有编号需在 [low,high][low, high][low,high] 范围内
约束条件
1. 范围约束
由于 a+(k−1)d≤higha+(k-1)d \leq higha+(k−1)d≤high 且 a≥lowa \geq lowa≥low ,可得:
- d≤high−lowk−1d \leq \frac{high - low}{k-1}d≤k−1high−low
- 对于给定的 ddd , aaa 的取值范围为: low≤a≤high−(k−1)dlow \leq a \leq high - (k-1)dlow≤a≤high−(k−1)d
2. 整除约束
kkk 个编号的和为:
S=k2[2a+(k−1)d] S = \frac{k}{2} \left[ 2a + (k-1)d \right] S=2k[2a+(k−1)d]
需要 k∣Sk \mid Sk∣S ,即:
k∣k2[2a+(k−1)d] k \mid \frac{k}{2} \left[ 2a + (k-1)d \right] k∣2k[2a+(k−1)d]
分情况讨论:
- 当 kkk 为奇数时: k2\frac{k}{2}2k 不是整数,原条件等价于 2∣(k−1)d2 \mid (k-1)d2∣(k−1)d
- 当 kkk 为偶数时: k2\frac{k}{2}2k 是整数,条件等价于 2∣[2a+(k−1)d]2 \mid \left[ 2a + (k-1)d \right]2∣[2a+(k−1)d] ,由于 2a2a2a 是偶数,即 2∣(k−1)d2 \mid (k-1)d2∣(k−1)d
合并条件:无论 kkk 奇偶,都需要 (k−1)d(k-1)d(k−1)d 是偶数。
方案数计算
对于每个合法的 ddd (满足 (k−1)d(k-1)d(k−1)d 是偶数且 d≤high−lowk−1d \leq \frac{high-low}{k-1}d≤k−1high−low ),首项 aaa 有:
cnt=max(0,high−(k−1)d−low+1) cnt = \max(0, high - (k-1)d - low + 1) cnt=max(0,high−(k−1)d−low+1)
种选择。
总方案数为所有合法 ddd 对应的 cntcntcnt 之和。
特殊情况 k=2k=2k=2
当 k=2k=2k=2 时,条件简化为 ddd 必须是偶数(因为 (2−1)d=d(2-1)d = d(2−1)d=d 需要是偶数)。此时:
- ddd 的取值范围: 2,4,6,…,high−low2, 4, 6, \dots, high-low2,4,6,…,high−low
- 对于每个偶数 ddd ,方案数: high−d−low+1high - d - low + 1high−d−low+1
设 n=high−lown = high - lown=high−low ,则方案数为:
∑i=1⌊n/2⌋(n−2i+1)=⌊n2⌋×n−⌊n2⌋2 \sum_{i=1}^{\lfloor n/2 \rfloor} (n - 2i + 1) = \left\lfloor \frac{n}{2} \right\rfloor \times n - \left\lfloor \frac{n}{2} \right\rfloor^2 i=1∑⌊n/2⌋(n−2i+1)=⌊2n⌋×n−⌊2n⌋2
这是一个 O(1)O(1)O(1) 的公式,避免了枚举。
算法设计
- 对于 k=2k=2k=2 :直接使用公式计算
- 对于 k>2k>2k>2 :枚举 ddd 从 111 到 high−lowk−1\frac{high-low}{k-1}k−1high−low
- 检查 (k−1)d(k-1)d(k−1)d 是否为偶数
- 计算 cnt=high−(k−1)d−low+1cnt = high - (k-1)d - low + 1cnt=high−(k−1)d−low+1
- 若 cnt>0cnt > 0cnt>0 则累加到答案
复杂度分析
- k=2k=2k=2 时: O(1)O(1)O(1)
- k>2k>2k>2 时:需要枚举 ddd ,最坏情况 k=3k=3k=3 , ddd 最多 high−low2≈107\frac{high-low}{2} \approx 10^72high−low≈107
- 题目保证输入不超过 100110011001 行,最坏情况 1000×107=10101000 \times 10^7 = 10^{10}1000×107=1010 次操作,但实际数据中 kkk 通常较大,使得 ddd 的范围很小,能够通过。
代码实现
// OOPS! They did it Again...
// UVa ID: 10799
// Verdict: Accepted
// Submission Date: 2025-12-13
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 计算 k=2 时的方案数
ll solveK2(ll low, ll high) {
ll n = high - low; // 注意:是 high-low,不是 high-low+1
ll m = n / 2; // 偶数 d 的个数
return m * n - m * m; // 等差数列求和公式
}
// 计算 k>2 时的方案数
ll solveK(ll low, ll high, ll k) {
ll maxD = (high - low) / (k - 1); // 最大公差
ll ans = 0;
for (ll d = 1; d <= maxD; ++d) {
// 条件:(k-1)*d 必须是偶数
if (((k - 1) * d) & 1) continue;
ll cnt = high - (k - 1) * d - low + 1;
if (cnt > 0) ans += cnt;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll low, high, k;
int caseNo = 1;
while (cin >> low >> high >> k) {
if (low == 0 && high == 0 && k == 0) break;
ll ans;
if (k == 2) ans = solveK2(low, high);
else ans = solveK(low, high, k);
cout << "Case " << caseNo++ << ": " << ans << "\n";
}
return 0;
}
关键点总结
- 数学推导是核心:正确理解等差数列求和与整除条件是解题关键
- 注意边界情况: k=2k=2k=2 时公式中的 nnn 是 high−lowhigh-lowhigh−low 而非 high−low+1high-low+1high−low+1
- 优化策略:对于 k=2k=2k=2 使用公式直接计算,避免枚举
- 代码简洁性:直接枚举法在 k>2k>2k>2 时足够高效,且避免了复杂边界处理
本题展示了如何将实际问题抽象为数学模型,并通过数学分析简化计算。正确的公式推导比复杂的算法优化更重要。
更多推荐



所有评论(0)