20180807 提高Day1 训练赛
题目目录
取数字
题意
题解
对于每个询问,我们统计 $a_i\bmod m$ 后每个结果的出现次数。
设 $g[i][j]$ 表示只用模 $m$ 结果为 $i$ 的数,加起来后模 $m$ 的结果为 $j$ 的方案数。
这个东西可以用组合数 $\mathcal O(m^2)$ 得出。
然后我们考虑 $dp[i][j]$ 表示用模 $m$ 结果为 $0$ 到 $i$ 的数,加起来后模 $m$ 的结果为 $j$ 的方案数。
我们转移的时候枚举 $0$ 到 $i-1$ 的和为 $k$,然后 $dp[i][j]$ 加上 $dp[i-1][k]\times g[i][(k-j+m)%m]$。
最后的答案就是 $dp[m-1][0]$ 。
调试记录
- 预处理阶乘没有模 $10^9+7$ 。
代码
#include <bits/stdc++.h> using namespace std; #define ll long long #define int long long const ll P = 1e9 + 7; ll frac[200005], fi[200005]; ll n, m, q, a[200005], dp[105][105], g[105][105], cnt[105]; inline ll PowerMod(ll x, ll y) { if (y == 0) return 1; if (y == 1) return x % P; if (y & 1) return PowerMod(x, y-1) * x % P; ll p = PowerMod(x, y>>1); return p * p % P; } inline ll inv(ll x) { return PowerMod(x, P-2); } inline ll C(ll x, ll y) { if (x < y) return 0; if (y==0 || y==x) return 1; return frac[x] * fi[y] % P * fi[x-y] % P; } inline ll rd() { int ret=0,nag=0;char ch;while(!isdigit(ch=getchar()))nag=ch=='-';ret=ch-'0';while(isdigit(ch=getchar()))(ret*=10)+=ch-'0';return nag?-ret:ret;} signed main() { //freopen("numbers.in", "r",stdin); //freopen("my.out","w",stdout); n = rd(); q = rd(); for (int i=1; i<=n; i++) a[i] = rd(); frac[0] = 1; for (int i=1; i<=200000; i++) frac[i] = frac[i-1] * i % P, fi[i] = inv(frac[i]); while (q--) { m = rd(); memset(cnt, 0, sizeof(cnt)); memset(g, 0, sizeof(g)); for (int i=1; i<=n; i++) ++cnt[(a[i] % m + m) % m]; for (int i=0; i<m; i++) { for (int j=0; j<=cnt[i]; j++) (g[i][j%m*i%m] += C(cnt[i], j)) %= P; } for (int i=0; i<m; i++) dp[0][i] = g[0][i] % P; for (int i=1; i<m; i++) { for (int j=0; j<m; j++) { dp[i][j] = 0; for (int k=0; k<m; k++) (dp[i][j] += (dp[i-1][k] % P * (g[i][(j-k+m) % m] % P)) % P) %= P; } } printf("%d\n", dp[m-1][0] % P); } return 0; }