原题链接:http://acm./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+7109+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⩽1000p⩽1000,但是数据范围是109109,背包显然不可做。但是,考虑到我们只需要凑出p的倍数,那么dp就只跟模p的余数有关系,那么我们读入时就可以将每个数模p缩小数据范围。
接下来,我们用二维数组dp[i][j]dp[i][j]表示前ii个数可以凑出余数为
dp[i][j]=dp[i−1][j]+dp[i−1][(j−x[i]+p)modp]dp[i][j]=dp[i−1][j]+dp[i−1][(j−x[i]+p)modp]
具体来讲,我们将每个数分为两个状态:
1.选了第ii个数,那么方案数就是前
2.不选,那么显然跟dp[i−1][j]dp[i−1][j]一样。
代码
#include<bits/stdc++.h>#define ll long longusing 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;}