原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5464
Clarke and problem
Problem Description
Clarke is a patient with multiple personality disorder. One day, Clarke turned into a student and read a book.
Suddenly, a difficult problem appears:
You are given a sequence of number a1,a2,…,an and a number p. Count the number of the way to choose some of number(choose none of them is also a solution) from the sequence that sum of the numbers is a multiple of p(0 is also count as a multiple of p). Since the answer is very large, you only need to output the answer modulo
109+7
10
9
+
7
Input
The first line contains one integer T(1≤T≤10) - the number of test cases.
T test cases follow.
The first line contains two positive integers n,p(1≤n,p≤1000)
The second line contains n integers a1,a2,…an(|ai|≤109).
Output
For each testcase print a integer, the answer.
Sample Input
1
2 3
1 2
Sample Output
2
Hint:
2 choice: choose none and choose all.
题目大意
克拉克是一名人格分裂患者。某一天,克拉克分裂成了一个学生,在做题。
突然一道难题难到了克拉克,这道题是这样的:
给你n个数,要求选一些数(可以不选),把它们加起来,使得和恰好是p的倍数(0也是p的倍数),求方案数。
对于n很小的时候,克拉克是能轻易找到的。然而对于n很大的时候,克拉克没有办法了,所以来求助于你。
题解
我们注意到 p⩽1000 p ⩽ 1000 ,但是数据范围是 109 10 9 ,背包显然不可做。但是,考虑到我们只需要凑出p的倍数,那么dp就只跟模p的余数有关系,那么我们读入时就可以将每个数模p缩小数据范围。
接下来,我们用二维数组
dp[i][j]
d
p
[
i
]
[
j
]
表示前
i
i
个数可以凑出余数为的方案数,那么就有状态转移方程:
具体来讲,我们将每个数分为两个状态:
1.选了第
i
i
个数,那么方案数就是前个数凑出余数为
(j−x[i]+p)
(
j
−
x
[
i
]
+
p
)
的方案数。
2.不选,那么显然跟
dp[i−1][j]
d
p
[
i
−
1
]
[
j
]
一样。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1005,mod=1e9+7;
int x[M],n,p;
ll dp[M][M];
void in()
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&p);
for(int i=1;i<=n;++i)
scanf("%d",&x[i]),x[i]%=p;
}
void ac()
{
dp[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=0;j<=p;++j)
dp[i][j]=(dp[i-1][j]+dp[i-1][(j-x[i]+p)%p])%mod;
printf("%lld\n",dp[n][0]);
}
int main()
{
int T;
scanf("%d",&T);
for(int i=1;i<=T;++i)
in(),ac();
return 0;
}