题目描述
有 nnn 个人参加游猎假期,活动安排需满足以下规则:
- 每天所有人分成若干组,每组人数完全相同,记为 kkk ,且 kkk 不得超过组织者规定的最大值 kmaxkmaxkmax 。
- 每天每个人所在的组,其组内其他 k−1k-1k−1 个人必须与之前任何一天的组员都不同(即任意两人至多同组一次)。
- 游猎结束时,任意两个人都必须曾在某一天同组过(即任意两人至少同组一次)。
给定 nnn 和 kmaxkmaxkmax ,要求找出满足条件的 最大可能 的每组人数 kkk(k≤kmaxk \le kmaxk≤kmax)以及对应的游猎天数。若问题无解,输出 No solution 。
输入格式
多组数据,每组一行,包含两个正整数 nnn 和 kmaxkmaxkmax 。输入直到文件结束。
输出格式
对每组数据,输出一行,格式为:k persons/group, d days
其中 kkk 为小组规模,ddd 为所需天数。若 d=1d = 1d=1 则输出 day ,否则输出 days 。若无解,输出 No solution 。
样例输入
4 1
4 2
4 3
4 4
样例输出
No solution
2 persons/group, 3 days
2 persons/group, 3 days
4 persons/group, 1 day
题目分析
问题转化
我们可以将问题抽象为一个 组合设计问题 :
- 有 nnn 个元素(参与者)。
- 需要设计若干“块”(每天的分组),每个块是若干个子集(组),每个子集大小均为 kkk ,且所有子集互不相交(每天每人只在一个组)。
- 要求任意两个元素 恰好同组一次(既满足“至少一次”又满足“至多一次”)。
这实际上是构造一个 可分解的平衡不完全区组设计(Resolvable Balanced Incomplete Block Design\texttt{Resolvable Balanced Incomplete Block Design}Resolvable Balanced Incomplete Block Design,简称 RBIBD\texttt{RBIBD}RBIBD),其中参数:
- v=nv = nv=n(元素个数)
- kkk(块大小)
- λ=1\lambda = 1λ=1(每对元素出现次数)
- 设计是可分解的(每天的分组是一个平行类,覆盖所有元素)
数学条件推导
设游猎进行 ddd 天,则:
-
每天产生的不同对数
每天有 nk\frac{n}{k}kn 个组(因为 kkk 必须整除 nnn),每组有 (k2)\binom{k}{2}(2k) 对不同参与者。
每天总对数为:
nk⋅(k2)=nk⋅k(k−1)2=n(k−1)2 \frac{n}{k} \cdot \binom{k}{2} = \frac{n}{k} \cdot \frac{k(k-1)}{2} = \frac{n(k-1)}{2} kn⋅(2k)=kn⋅2k(k−1)=2n(k−1) -
需要覆盖的总对数
所有参与者两两配对的总数为 (n2)=n(n−1)2\binom{n}{2} = \frac{n(n-1)}{2}(2n)=2n(n−1) 。 -
天数公式
由于每对恰好出现一次,所以:
d⋅n(k−1)2=n(n−1)2 d \cdot \frac{n(k-1)}{2} = \frac{n(n-1)}{2} d⋅2n(k−1)=2n(n−1)
解得:
d=n−1k−1 d = \frac{n-1}{k-1} d=k−1n−1
因此,必要条件为:
- k∣nk \mid nk∣n(保证每天组数为整数)
- (k−1)∣(n−1)(k-1) \mid (n-1)(k−1)∣(n−1)(保证天数为整数)
存在性条件
上述两个整除条件是必要条件。对于组合设计,通常还需要满足一些其他存在性条件(例如 nnn 和 kkk 的数值关系),但本题的输入数据似乎默认:只要满足这两个整除条件,就存在合法的分组方案。因此我们只需检查这两个条件即可。
特殊情况
- k=nk = nk=n
所有人一组,一天即可完成。此时公式 d=n−1k−1d = \frac{n-1}{k-1}d=k−1n−1 中 k−1=n−1k-1 = n-1k−1=n−1 ,天数为 111 。注意 k=nk = nk=n 时必然满足两个整除条件。 - k=1k = 1k=1
每人一组,无法满足“任意两人同组”的条件,因此只有当 n=1n = 1n=1 时才可能有效(此时自己和自己“同组”)。 - n=1n = 1n=1
只有一个人,任意 k≥1k \ge 1k≥1 都无意义,通常规定 k=1,d=1k = 1, d = 1k=1,d=1 。
解题步骤
对于给定的 nnn 和 kmaxkmaxkmax :
- 若 n≤kmaxn \le kmaxn≤kmax ,则最优解为 k=nk = nk=n(一天完成)。
- 否则,从 k=kmaxk = kmaxk=kmax 向下枚举到 222 ,找到第一个满足:
- n%k==0n \% k == 0n%k==0
- (n−1)%(k−1)==0(n-1) \% (k-1) == 0(n−1)%(k−1)==0
的 kkk 。
- 若枚举后仍未找到,且 n=1n = 1n=1 且 kmax≥1kmax \ge 1kmax≥1 ,则 k=1k = 1k=1 。
- 若最终未找到任何 kkk ,输出
No solution。 - 否则计算天数 ddd:
- 若 k=nk = nk=n 或 k=1k = 1k=1,则 d=1d = 1d=1
- 否则 d=(n−1)/(k−1)d = (n-1) / (k-1)d=(n−1)/(k−1)
- 按格式输出,注意
day和days的单复数。
算法复杂度
由于 kmaxkmaxkmax 可能很大,最坏情况下需枚举 O(kmax)O(kmax)O(kmax) 次。但 kmaxkmaxkmax 本身也是输入值,在合理的输入范围内(例如 10910^9109 以内)直接枚举是可行的。
每个检查为 O(1)O(1)O(1) ,总体时间复杂度 O(kmax)O(kmax)O(kmax) 每组数据。
代码实现
// Safari Holiday
// UVa ID: 799
// Verdict: Accepted
// Submission Date: 2026-01-24
// UVa Run Time: 0.000s
//
// 版权所有(C)2026,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main() {
long long n, kmax;
while (cin >> n >> kmax) {
long long bestK = -1;
// 如果允许所有人一组
if (n <= kmax) bestK = n;
else {
// 从大到小搜索第一个满足整除条件的 k
for (long long k = kmax; k >= 2; --k)
if (n % k == 0 && (n - 1) % (k - 1) == 0) {
bestK = k;
break;
}
}
// 处理 n=1 的特殊情况
if (n == 1 && kmax >= 1) bestK = 1;
if (bestK == -1) cout << "No solution\n";
else {
long long days;
if (bestK == n || bestK == 1) days = 1;
else days = (n - 1) / (bestK - 1);
cout << bestK << " persons/group, " << days << " day";
if (days > 1) cout << 's';
cout << '\n';
}
}
return 0;
}
总结
本题关键在于将实际问题转化为组合设计模型,并推导出两个简洁的整除条件:
- k∣nk \mid nk∣n
- (k−1)∣(n−1)(k-1) \mid (n-1)(k−1)∣(n−1)
然后通过从大到小枚举 kkk 找到满足条件且不超过 kmaxkmaxkmax 的最大值,最后计算天数并输出即可。注意处理好边界情况 k=nk = nk=n 和 n=1n = 1n=1 。

1136

被折叠的 条评论
为什么被折叠?



