原题链接
题目大意
已知
k
(
1
≤
k
≤
64
)
k(1 \leq k \leq 64)
k(1≤k≤64) ,对于每个仅包含
[
0
,
k
−
1
]
[0,k-1]
[0,k−1] 区间内的整数的数组,定义其优美度为非空连续子数组之和为
k
k
k 的倍数的数量。
求有多少长度为
n
n
n 的数组,其优美度为
t
t
t ,答案对
998244353
998244353
998244353 取模。
题解
对于快速求区间和,采用前缀和算法,
由于要求
k
k
k 的倍数,所以我们可以把前缀和
m
o
d
k
mod\ k
mod k 容易想到,由取模而得到的前缀和是单一的,并且可以通过倒推得到。
如何由前缀和快速判断是否优美
设该数组的前缀和为
s
u
m
sum
sum ,则其区间和为
s
u
m
r
−
s
u
m
l
−
1
sum_r-sum_{l-1}
sumr−suml−1,由取模操作可以得到,当
s
u
m
l
−
1
=
=
s
u
m
r
sum_{l-1}==sum_r
suml−1==sumr 时该区间是优美的。所以不难由前缀和,通过组合数学算出优美度。设前缀和为
i
i
i 的有
d
i
d_i
di 个,所以由一个已知数组可以得到的优美度为
∑
C
d
i
2
\sum C_{d_i}^2
∑Cdi2。
如何快速构造符合的数组
由于前缀和和数组一一对应,考虑构造满足的前缀和,可以设
D
P
DP
DP 解决,
设
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k 为仅使用
i
i
i 个数字内占用了
j
j
j 个位置,其优美度为
k
k
k 的数组数量。
考虑转移
每添加一个新的数字的可能数,在
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k 中,设加入的数字数为
x
x
x ,剩余的空位显然为
n
−
y
n-y
n−y,所以转移式为
d
p
i
+
1
,
j
+
x
,
k
+
C
x
2
+
=
d
p
i
,
j
,
k
∗
C
n
−
y
x
(
1
≤
x
≤
n
−
y
)
dp_{i+1,j+x,k+C_{x}^2}+=dp_{i,j,k}*C_{n-y}^{x}(1\leq x \leq n-y)
dpi+1,j+x,k+Cx2+=dpi,j,k∗Cn−yx(1≤x≤n−y)
提前特殊处理初始化
i
=
0
i=0
i=0 的情况,
暴力枚举四维即可,复杂度为
O
(
6
4
5
)
=
O(64^5)=
O(645)= 能过 。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=70;
const int mod=998244353;
int dp[N][N][N*N];
int C[N][N];
int inc[N],fac[N];
int n,t,k;
int ksm(int a,int b)
{
int ret=1;
while(b)
{
if(b&1)
ret=1ll*ret*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ret;
}
void init()
{
for(int i=0;i<N;i++) //打表组合数
{
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
int main()
{
init();
scanf("%d%d%d",&n,&k,&t);
for(int i=0;i<=n;i++) //初始化,容易算得i个零的情况可能为C(n,i)
dp[0][i][C[i+1][2]]=C[n][i];
for(int i=0;i<k-1;++i) //转移
{
for(int j=0;j<=n;++j)
for(int l=0;l<=t;++l)
{
if(!dp[i][j][l])
continue;
for(int x=0;j+x<=n&&l+C[x][2]<=t;++x)
dp[i+1][j+x][l+C[x][2]]=(1ll*dp[i+1][j+x][l+C[x][2]]+dp[i][j][l]*C[n-j][x])%mod;
}
}
printf("%d",dp[k-1][n][t]);
}