题解:AtCoder AT_awc0130_c Choosing a Club Activity

【题目来源】

AtCoder:C - Choosing a Club Activity

【题目描述】

Takahashi is a new high school student who is trying to decide which club activities to join.

There are \(N\) club activities at Takahashi's school, and each club activity has an "activity point" value assigned to it. The activity points of the \(i\)-th club activity is \(A_i\).

Takahashi can join multiple club activities simultaneously, but he has his own particular preferences.

  • He considers a combination "well-balanced" if the total activity points of the chosen club activities is a multiple of \(K\).
  • Joining no club activities (the empty combination) is not allowed, as it would make for a lonely high school life.

Takahashi wants to know how many well-balanced combinations of club activities exist.

Among all ways to choose \(1\) or more club activities from the \(N\) available, find the total number of ways such that the sum of activity points of the chosen club activities is a multiple of \(K\). Since the answer can be very large, output it modulo \(10^9 + 7\).

高橋是一名高中新生,正在考虑加入哪些社团活动。

高橋的学校有 \(N\) 个社团活动,每个社团活动都被分配了一个"活动点数"。第 \(i\) 个社团活动的活动点数为 \(A_i\)

高橋可以同时加入多个社团活动,但他有自己的特殊偏好。

  • 如果所选社团活动的活动点数之和是 \(K\) 的倍数,他认为该组合是"均衡的"。
  • 不加入任何社团活动(空组合)是不允许的,因为这会让高中生活变得孤独。

高橋想知道有多少种均衡的社团活动组合。

\(N\) 个社团活动中选择 \(1\) 个或更多社团活动的所有方式中,求所选社团活动的活动点数之和为 \(K\) 的倍数的方式总数。由于答案可能很大,输出它对 \(10^9 + 7\) 取模的结果。

【输入】

\(N\) \(K\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)

  • The first line contains \(N\), the number of club activities, and \(K\), the value for the multiple criterion, separated by a space.
  • The second line contains \(A_1, A_2, \ldots, A_N\), the activity points of each club activity, separated by spaces.

【输出】

Output in a single line the number of ways to choose \(1\) or more club activities such that the total activity points is a multiple of \(K\), modulo \(10^9 + 7\).

【输入样例】

3 3
1 2 3

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(N\) 个社团活动,每个有活动点数 \(A_i\)。求选择至少 \(1\) 个社团的方案数,使得所选社团的活动点数之和为 \(K\) 的倍数。答案对 \(10^9+7\) 取模。这是一个模意义下的01背包(子集和计数)问题,关键在于用 \(f[i][j]\) 记录前 \(i\) 个社团中总点数模 \(K\)\(j\) 的方案数。

  2. 算法选择

    • 线性DP(一维扩展)\(f[i][j]\) 表示考虑前 \(i\) 个社团,总点数模 \(K\)\(j\) 的方案数
    • 状态转移:对每个社团,选择"不选"或"选"两种决策,分别贡献到对应的余数状态
    • 模运算处理:利用 \((a + b) \bmod K = ((a \bmod K) + (b \bmod K)) \bmod K\),将大数求和转化为余数转移
  3. 关键步骤

    • 初始化\(f[0][0] = 1\)(考虑 \(0\) 个社团时,总点数模 \(K\)\(0\) 的方案数为 \(1\),即空集)
    • 状态转移\(i\)\(1\)\(N\)\(j\)\(0\)\(K-1\)):
      • 不选第 \(i\) 个社团:贡献 \(f[i-1][j]\)
      • 选第 \(i\) 个社团:需要从前一状态的余数 \((j - A_i \bmod K + K) \bmod K\) 转移,贡献 \(f[i-1][(j - A_i \% K + K) \% K]\)
      • \(f[i][j] = (f[i-1][j] + f[i-1][(j - A_i \% K + K) \% K]) \bmod mod\)
    • 输出答案\((f[N][0] - 1 + mod) \% mod\),减去空集的 \(1\) 种方案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot K)\),外层 \(N\) 个社团,内层 \(K\) 个余数状态
    • 空间复杂度:\(O(N \cdot K)\),DP 数组 \(f[N][K]\)(可滚动优化为 \(O(K)\)
  5. 模意义下子集和计数的核心思想

    • 余数状态压缩:不直接记录总和(可能极大),而是记录模 \(K\) 的余数,将无限状态空间压缩为 \(K\) 个状态
    • 01背包的计数版本:每个物品(社团)有"选"和"不选"两种决策,与经典01背包类似,但目标是计数而非最值
    • 模运算的加减法技巧\((j - A_i \% K + K) \% K\) 确保结果在 \([0, K-1]\) 范围内,避免负数下标
    • 空集处理:DP 过程中空集被计入 \(f[N][0]\),最终答案需减去 \(1\) 排除空集
    • 适用于"子集和模某数计数"问题,核心在于用余数作为状态维度,将指数级枚举降为多项式时间

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 100005, mod = 1e9+7; // 定义最大数组大小和模数
int n, k; // n: 社团活动数量, k: 倍数判定基数
int a[N]; // a[i]: 第i个社团活动的活动点数
int f[N][105]; // f[i][j]: 考虑前i个社团,总点数模k余j的方案数

int main()
{
    cin >> n >> k; // 读入社团数量和倍数基数
    for (int i=1; i<=n; i++) // 循环读入每个社团的活动点数
        cin >> a[i];
    f[0][0] = 1; // 初始化:考虑0个社团时,总点数模k余0的方案数为1(空集)
    for (int i=1; i<=n; i++) // 外层循环:枚举每个社团
        for (int j=0; j<k; j++) // 内层循环:枚举模k的所有余数
        {
            // 状态转移:f[i][j] = 不选第i个社团的方案数 + 选第i个社团的方案数
            // 不选第i个社团:方案数为f[i-1][j]
            // 选第i个社团:需要从前一状态的余数(j - a[i]%k + k) % k转移过来
            f[i][j] = (f[i-1][j] + f[i-1][(j-a[i]%k+k)%k]) % mod;
        }
    // 输出结果:f[n][0]表示总点数模k余0的所有方案数(包含空集)
    // 减去1去掉空集,加mod防止负数,再取模
    cout << (f[n][0] - 1 + mod) % mod<< endl;
    return 0;
}

【运行结果】

3 3
1 2 3
3
posted @ 2026-08-10 12:24  团爸讲算法  阅读(5)  评论(0)    收藏  举报