【题目描述】
给定整数 n,m, k,和一个长度为m+1 的正整数数组 v0, v1, · · · ,vm。
对于一个长度为 n,下标从 1 开始且每个元素均不超过 m 的非负整数序列 {ai},我们定义它的权值为 va1 × va2 × · · · × van。
当这样的序列 {ai} 满足整数 S = 2a1 + 2a2 + · · · + 2an 的二进制表示中 1 的个数不超过 k 时,我们认为 {ai} 是一个合法序列。
计算所有合法序列 {ai} 的权值和对 998244353 取模的结果。
【输入】
输入的一行是三个整数 n, m, k。
第二行 m+1 个整数,分别是 v0, v1, · · · , vm。
【输出】
仅一行一个整数,表示所有合法序列的权值和对 998244353 取模的结果。
【输入样例】
5 1 1
2 1
【输出样例】
40
【提示】
【样例解释】
由于 k=1,而且由 n≤S≤n×2m 知道 5≤S≤10,合法的 S 只有一种可能:
S=8,这要求 a 中必须有 2 个 0 和 3 个 1,于是有 (52)=10 种可能的序列,每种序列的贡献都是 v20v31=4,权值和为 10×4=40。
对所有测试点保证 1≤k≤n≤30, 0≤m≤100, 1≤vi<998244353。
测试点 | n | k | m |
1 ∼ 4 | = 8 | ≤ n | = 9 |
5 ∼ 7 | = 30 | = 7 | |
8 ∼ 10 | = 12 | ||
11 ∼ 13 | = 1 | = 100 | |
14 ∼ 15 | = 5 | ≤ n | = 50 |
16 | = 15 | = 100 | |
17 ∼ 18 | = 30 | = 30 | |
19 ∼ 20 | = 100 |
代码:
#include <stdio.h>
#include <string.h>
#define NN 103
#define NN2 32
#define MM 998244353
#define I64 long long
I64 f[2][NN2][NN2][NN2];
I64 (*f1)[NN2][NN2], (*f2)[NN2][NN2];
I64 pow_v[NN][NN2];
void cal_c_powv(I64 c[][NN2], int v[], int m, int n)
{
int i, j;
c[0][0] = 1;
for (i=1; i<NN2; i++) {
c[i][0] = 1;
for (j=1; j<i; j++) c[i][j] = (c[i-1][j]+c[i-1][j-1])%MM;
c[i][i] = 1;
}
for (i=0; i<=m; i++) {
pow_v[i][0]=1;
for (j=1; j<=n; j++) pow_v[i][j] = pow_v[i][j-1]*v[i]%MM;
}
}
int num_one(int s)
{
int cnt=0;
for (; s; s/=2) cnt += s%2;
return cnt;
}
int main()
{
int N, M, K, v[NN], n, k, i, j, s;
I64 c[NN2][NN2];
scanf("%d%d%d", &N, &M, &K);
for (i=0; i<=M; i++) scanf("%d", v+i);
cal_c_powv(c, v, M, N);
f1 = f[0];
f2 = f[1];
memset(f, 0, sizeof(f));
for (n = 0; n <= N; n++)
for (s = 0; s <= N-n; s++)
for (k = num_one(s+n); k<=K; k++)
f2[n][k][s] = pow_v[M][n];
for (i=M-1; i>=0; i--)
{
I64 (*tmp)[NN2][NN2] = f1;
f1 = f2; f2 = tmp;
memset(f2, 0, sizeof(I64)*NN2*NN2*NN2);
f2[0][0][0] = 1;
for (n=0; n<=N; n++) for (k=1; k<=K; k++) for (s=0; s<=(N-n)/2; s++)
{
for (j=0; j<=n; j++) {
f2[n][k][s] += pow_v[i][j]*f1[n-j][k-(s+j)%2][(s+j)/2]%MM*c[n][j]%MM;
}
f2[n][k][s] %= MM;
}
}
printf("%lld\n", f2[N][K][0]);
return 0;
}