问题描述
克拉克是一名人格分裂患者。某一天,克拉克分裂成了一个学生,在做题。 突然一道难题难到了克拉克,这道题是这样的: 给你n个数,要求选一些数(可以不选),把它们加起来,使得和恰好是p的倍数(0也是p的倍数),求方案数。 对于n很小的时候,克拉克是能轻易找到的。然而对于n很大的时候,克拉克没有办法了,所以来求助于你。
输入描述
第一行一个整数T(1≤T≤10),表示数据的组数。 每组数据第一行是两个正整数n,p(1≤n,p≤1000)。 接下来的一行有n个整数ai(∣ai∣≤109),表示第i个数。
输出描述
对于每组数据,输出一个整数,表示问题的方案数,由于答案很大,所以求出对109+7的答案即可。
输入样例
1 2 3 1 2
输出样例
2
Hint
有两种方案:什么也不选;全都选。 如果想到用dp,这个题目还是很简单的 一位dp,两层循环,表示前i个数组成和为j的情况有多少种, 还要注意的一点是一个数不能用两次#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <ctime> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define INF 0x3f3f3f3f #define inf -0x3f3f3f3f #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mem0(a) memset(a,0,sizeof(a)) #define mem1(a) memset(a,-1,sizeof(a)) #define mem(a, b) memset(a, b, sizeof(a)) typedef long long ll; const int MOD=1e9+7; ll dp[2000]; int num[2000]; ll vis[2000][1001]; int main(){ int t; int n,p; scanf("%d",&t); while(t--){ mem0(dp); dp[0]=1; mem0(vis); scanf("%d%d",&n,&p); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); if(num[i]>=0) num[i]=num[i]%p; else num[i]=p+num[i]%p; } for(int j=1;j<=n;j++){ for(int i=0;i<p;i++){ int x=(num[j]+i)%p; if(x<0) x+=p; dp[x]=(dp[x]+dp[i])%MOD; if(vis[i][j]!=0){ dp[x]=(dp[x]-vis[i][j])%MOD; if(dp[x]<0) dp[x]+=MOD; } vis[x][j]=(vis[x][j]+dp[i]-vis[i][j])%MOD; if(vis[x][j]<0) vis[x][j]+=MOD; } } printf("%I64d\n",dp[0]); } return 0; }