UVa 799 Safari Holiday

题目描述

nnn 个人参加游猎假期,活动安排需满足以下规则:

  1. 每天所有人分成若干组,每组人数完全相同,记为 kkk ,且 kkk 不得超过组织者规定的最大值 kmaxkmaxkmax
  2. 每天每个人所在的组,其组内其他 k−1k-1k1 个人必须与之前任何一天的组员都不同(即任意两人至多同组一次)。
  3. 游猎结束时,任意两个人都必须曾在某一天同组过(即任意两人至少同组一次)。

给定 nnnkmaxkmaxkmax ,要求找出满足条件的 最大可能 的每组人数 kkkk≤kmaxk \le kmaxkkmax)以及对应的游猎天数。若问题无解,输出 No solution

输入格式

多组数据,每组一行,包含两个正整数 nnnkmaxkmaxkmax 。输入直到文件结束。

输出格式

对每组数据,输出一行,格式为: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 天,则:

  1. 每天产生的不同对数
    每天有 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)=kn2k(k1)=2n(k1)

  2. 需要覆盖的总对数
    所有参与者两两配对的总数为 (n2)=n(n−1)2\binom{n}{2} = \frac{n(n-1)}{2}(2n)=2n(n1)

  3. 天数公式
    由于每对恰好出现一次,所以:
    d⋅n(k−1)2=n(n−1)2 d \cdot \frac{n(k-1)}{2} = \frac{n(n-1)}{2} d2n(k1)=2n(n1)
    解得:
    d=n−1k−1 d = \frac{n-1}{k-1} d=k1n1

因此,必要条件为:

  • k∣nk \mid nkn(保证每天组数为整数)
  • (k−1)∣(n−1)(k-1) \mid (n-1)(k1)(n1)(保证天数为整数)

存在性条件

上述两个整除条件是必要条件。对于组合设计,通常还需要满足一些其他存在性条件(例如 nnnkkk 的数值关系),但本题的输入数据似乎默认:只要满足这两个整除条件,就存在合法的分组方案。因此我们只需检查这两个条件即可。

特殊情况

  1. k=nk = nk=n
    所有人一组,一天即可完成。此时公式 d=n−1k−1d = \frac{n-1}{k-1}d=k1n1k−1=n−1k-1 = n-1k1=n1 ,天数为 111 。注意 k=nk = nk=n 时必然满足两个整除条件。
  2. k=1k = 1k=1
    每人一组,无法满足“任意两人同组”的条件,因此只有当 n=1n = 1n=1 时才可能有效(此时自己和自己“同组”)。
  3. n=1n = 1n=1
    只有一个人,任意 k≥1k \ge 1k1 都无意义,通常规定 k=1,d=1k = 1, d = 1k=1,d=1

解题步骤

对于给定的 nnnkmaxkmaxkmax

  1. n≤kmaxn \le kmaxnkmax ,则最优解为 k=nk = nk=n(一天完成)。
  2. 否则,从 k=kmaxk = kmaxk=kmax 向下枚举到 222 ,找到第一个满足:
    • n%k==0n \% k == 0n%k==0
    • (n−1)%(k−1)==0(n-1) \% (k-1) == 0(n1)%(k1)==0
      kkk
  3. 若枚举后仍未找到,且 n=1n = 1n=1kmax≥1kmax \ge 1kmax1 ,则 k=1k = 1k=1
  4. 若最终未找到任何 kkk ,输出 No solution
  5. 否则计算天数 ddd
    • k=nk = nk=nk=1k = 1k=1,则 d=1d = 1d=1
    • 否则 d=(n−1)/(k−1)d = (n-1) / (k-1)d=(n1)/(k1)
  6. 按格式输出,注意 daydays 的单复数。

算法复杂度

由于 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;
}

总结

本题关键在于将实际问题转化为组合设计模型,并推导出两个简洁的整除条件:

  1. k∣nk \mid nkn
  2. (k−1)∣(n−1)(k-1) \mid (n-1)(k1)(n1)

然后通过从大到小枚举 kkk 找到满足条件且不超过 kmaxkmaxkmax 的最大值,最后计算天数并输出即可。注意处理好边界情况 k=nk = nk=nn=1n = 1n=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值