【题解】 [USACO 2009 Mar] Cow Frisbee Team S

题目描述

题目大意

\(N\) 个整数中取若干个(不能不选),且取的数之和为 \(F\) 的倍数的总方案数对\(10^8\)取余的值

思路

这道题是一道二维线性DP。那么根据线性DP的解题方法
首先,找出题目的阶段性。这道题显而易见方案数随着选择的个数而变化,那么这道题就以选择的个数作为阶段。
接着,根据阶段定义状态。这道题的状态可以定义为 \(dp_{i, j}\) 表示 从前 \(i\) 个数中取,且取的数之和对 \(F\) 取余的值为 \(j\)方案数对\(10^8\)求余的值
然后,根据状态分析状态转移方程。每一个状态的情况可以分类讨论:

  • 不取第 \(i\) 个数,则从前 \(i - 1\) 个数中取的数之和对 \(F\) 取余的值亦要为 \(j\),那么不取第 \(i\) 个数,余数仍为 \(j + 0 = j\)
  • 不取第 \(i\) 个数,则从前 \(i - 1\) 个数中取的数之和对 \(F\) 取余的值要为 \((j - a_i + F) \mod F\),那么取第 \(i\) 个数,余数变为 \((j - a_i + F) \mod F + a_i = j\)

综上所述,状态转移方程为:$$dp_{i, j} = (dp_{i, j} + dp_{i - 1, j} + dp_{i - 1, (j - a_i + F) \mod F}) \mod 10^8$$
最后,定义初值。即 $$dp_{i, a_i \mod F} = 1$$
最终输出的是从前 \(n\) 个数中取,且取的数之和对 \(F\) 取余的值为 \(0\)总方案数对\(10^8\)取余的值,即 \(dp_{n, 0}\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2005, F = 1005, MOD = 1e8;
int n, f, a[N];
int dp[N][F];

int main()
{
    scanf("%d%d", &n, &f);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        a[i] %= f; // 预处理取模
    }

    // 定义初值
    for (int i = 1; i <= n; i ++ ) dp[i][a[i]] = 1;
    // 状态转移
    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j < f; j ++ )
            dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i - 1][(j - a[i] + f) % f]) % MOD;
    printf("%d\n", dp[n][0]); // 输出答案

    return 0;
}
posted @ 2024-05-26 07:55  T_泓  阅读(6)  评论(0编辑  收藏  举报