TIOJ-1628

組合布丁

https://tioj.ck.tp.edu.tw/problems/1628

Description

記得在快樂暑假營開始前,你曾經說過:「只要我有一次比賽沒有破台,就要請全快樂營的人吃布丁。」
好吧,蚯蚓太威了,你終究是沒有破台。
根據小道消息,你得知了這次的快樂暑假營總共有 $n$ 個人報名,
但是實際上會出席的只有 $k$ 個人,因此你只要請 $k$ 個人吃布丁就好。
而報名的第 $i$ 個人只會願意吃 $t_i$ 口味的布丁(用一個 int 範圍內的整數表示)。
假設你不確定究竟誰會出席,那有幾種不同的布丁組合可能會出現在你的採買清單上 ?
喔對了,因為答案可能太大了,所以你決定只要知道答案除以 $M$ 的餘數就好。
輸入包含多筆測資

$$
1 \leq n, k \leq 5000, 1 \leq M < 2^{31}
$$

Solution

兩種布丁組合不同,若且唯若某一種布丁的數量不同
因此我們枚舉每個布丁的數量去做計數背包就好了
可以用前綴和甚至FFT加速(?)不過FFT應該不會比較快www

AC code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;

int t[5025], n, k, M;
long long dp[5025];
void go(int maxcnt) {
    for(int i = k; i >= 0; i--) {
        long long sum = 0;
        for(int j = 0; j <= i && j <= maxcnt; j++) {
            sum = (sum + dp[i-j])%M;
        }
        dp[i] = sum;
    }
}
signed main() {
    while(cin >> n >> k >> M) {
        dp[0] = 1;
        for(int i = 1; i <= k; i++) dp[i] = 0;
        for(int i = 0; i < n; i++) cin >> t[i];
        sort(t,t+n);
        for(int i = 0, j; i < n; i = j) {
            for(j = i; j < n; j++) if(t[i] != t[j]) break;
            go(j-i);
        }
        cout << dp[k] << '\n';
    }
}
comments powered by Disqus