CodeChef--Cards, bags and coins

数列与组合数学在概率问题中的应用
本文探讨了一道涉及数列、组合数学及概率论的编程问题,通过预处理和动态规划方法,有效地计算出了特定条件下赢得游戏的概率。文章详细介绍了问题背景、输入输出规范、解题思路,并提供了AC代码,展示了从理论到实现的过程。

题目链接

Yet another game from chef. Chef gives you N cards and M bags. Each of the N cards has an integer written on it. Now chef asks you to close your eyes and choose a subset of them. He then sums the numbers written on chosen cards, takes its absolute value and gives you those many coins. You win the game if you can divide these coins into M bags with each bag having equal share. As a first step to calculate the probability of winning, you would like to know the number of different subsets which will make you win. Note that all the cards are of different color, so even if 2 cards have the same number written on it, they are still considered as different cards.

Input

The first line of the input contains an integer T denoting the number of test cases. The description of T test cases follows.
First line of each test case contains two integers N and QQ denotes the number of queries to be answered. Second line of each test case contains N integers, the numbers written on cards.
Following Q lines contain an integer M.

Output

For each query output the required Answer modulo 1000000009. Answer is the number of subsets that will ensure you win.

Constraints

  • 1 ≤ T ≤ 3
  • 1 ≤ N ≤ 100000
  • 1 ≤ Q ≤ 30
  • 1 ≤ M ≤ 100
  • -10^9 ≤ Number on card ≤ 10^9

Example

Input
2
5 1
1 2 -1 4 5
9
5 2
1 2 3 4 5
5
15

Output
4
8
2

Explanation

Test Case #1, Query #1
{}, {1,-1}, {1,-1,4,5}, {4,5} are winning subsets. Sums are 0, 0, 9, 9 respectively.

Test Case #2, Query #1
{}, {5}, {1,4}, {2,3}, {1,4,5}, {2,3,5}, {1,2,3,4}, {1,2,3,4,5} are winning subsets. Sums are 0, 5, 5, 5, 10, 10, 10, 15 respectively.

Test Case #2, Query #2
{}, {1,2,3,4,5} are winning subsets. Sums are 0 and 15 respectively.

Author's Note

Time Limit is not very strict (Yes, not very loose either) if correct Algorithm is used.Author's solution passes with 2 sec Time Limit (C++ solution, using scanf and printf).
Maximum Input File Size < 4MB.

题意:给出n个数,然后还有一个数字m,问有多少种方法可以从n个数中选出一些数使得这些数的和是m的倍数。

很好的一道题,学到了很多。首先对于自己的基础之差感动汗颜。

第一,对C(n, k)的打表。。。这里还是说下自己的想法吧,如果n<10^3,这个数据量基本是可以用递推的,二维数组C[n][k]记录。

像这题的n<10^5,显然无法开出巨表,所以可以使用C(n, k) = n!/(k! * (n -k)!)..这样就可以通过记录n!和n!的逆来进行求解,

如果题目给出的模数是个质数,就可以通过费马小定理很方便的求出一个数的逆元,当然扩展gcd也是可以的。

其次,对于这题的思路,看到题目和数据范围就应该想到实际上真正的数据范围就只有[0, m)...所以每次询问直接将A[i]模m,然后

得到每个数出现的次数。这样就可以得到dp的基本模型了。

dp[i][j]表示从0到i这些数中选一些数模m为j的方案数,然后dp[i][j]可以从dp[i-1][0..m-1]得到。

 Accepted Code:

 1 /*************************************************************************
 2     > File Name: ANUCBC.cpp
 3     > Author: Stomach_ache
 4     > Mail: sudaweitong@gmail.com
 5     > Created Time: 2014年09月04日 星期四 14时13分00秒
 6     > Propose: 
 7  ************************************************************************/
 8 #include <cmath>
 9 #include <string>
10 #include <cstdio>
11 #include <fstream>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 using namespace std;
16 /*Let's fight!!!*/
17 
18 #define rep(i, n) for (int i = (0); i < (n); i++)
19 #define FOR(i, a, b) for (int i = (a); i <= (b); i++)
20 const int MAX_N = 100050;
21 const int MAX_M = 101;
22 const int MOD = 1e9 + 9;
23 typedef long long LL;
24 LL fact[MAX_N], ifact[MAX_N];
25 
26 LL pow_mod(LL a, LL b) {
27       LL res = 1;
28     while (b) {
29           if (b & 1) res = (res * a) % MOD;
30         a = (a * a) % MOD;
31         b >>= 1;
32     }
33     return res;
34 }
35 
36 //fact and ifact
37 void init() {
38       fact[0] = fact[1] = ifact[0] = ifact[1] = 1;
39     FOR (i, 2, MAX_N - 50) {
40           fact[i] = (fact[i - 1] * i) % MOD;
41         ifact[i] = (ifact[i - 1] * pow_mod(i, MOD - 2)) % MOD;
42     }
43 }
44 
45 int C(int n, int k) {
46       return (fact[n] * ifact[k] % MOD) * ifact[n - k] % MOD;
47 }
48 
49 int A[MAX_N], cnt[MAX_M];
50 LL dp[MAX_M][MAX_M], choose[MAX_M][MAX_M];
51 
52 int main(void) {
53     init(); // precomputation 
54 
55     ios::sync_with_stdio(false);
56     int T;
57     cin >> T;
58     while (T--) {
59         int N, Q, M;
60         cin >> N >> Q;
61         rep (i, N) cin >> A[i];
62         while (Q--) {
63             cin >> M;
64             memset(cnt, 0, sizeof(cnt));
65             rep (i, N) cnt[(A[i] % M + M) % M]++;
66 
67             memset(choose, 0, sizeof(choose));
68             rep (i, M) FOR (j, 0, cnt[i]) {
69                   choose[i][j * i % M] = (choose[i][j * i % M] + C(cnt[i], j)) % MOD;
70             }
71 
72             memset(dp, 0, sizeof(dp));
73             dp[0][0] = choose[0][0];
74             FOR (i, 1, M-1) rep (j, M) rep (k, M) dp[i][j] = (dp[i][j] + dp[i - 1][(j - k + M) % M] * choose[i][k]) % MOD;
75 
76             cout << dp[M - 1][0] << endl;
77         }
78     }
79     return 0;
80 }

 

转载于:https://www.cnblogs.com/Stomach-ache/p/3956953.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值