"蔚来杯"2022牛客暑期多校训练营7-J Melborp Elcissalc
原题题面:https://ac.nowcoder.com/acm/contest/33192/J
题目大意
已知 k ( 1 ≤ k ≤ 64 ) k(1\le k\le 64) k(1≤k≤64),对于每个仅包含 [ 0 , k − 1 ] [0,k-1] [0,k−1]区间内的整数的数组,定义其优美度为非空连续子数组之和为 k k k的倍数的数量。
求有多少长度为 n ( 1 ≤ n ≤ 64 ) n(1\le n\le 64) n(1≤n≤64)的数组,其优美度为 t ( 0 ≤ t ≤ n ( n + 1 ) 2 ) t(0\le t\le\frac{n(n+1)}{2}) t(0≤t≤2n(n+1)),答案对 998244353 998244353 998244353取模。
解题思路
考虑如何求给定一个序列,求其中和为 k k k的倍数的区间的个数,先求一遍前缀和,然后各取模 k k k,不影响结果 ,求出 s u m i = ( s u m i − 1 + a i ) % m o d sum_i=(sum_{i-1}+a_i)\%mod sumi=(sumi−1+ai)%mod。
发现若任意
s
u
m
i
=
s
u
m
j
(
i
≤
j
)
sum_i=sum_j(i\le j)
sumi=sumj(i≤j),则区间
[
i
,
j
]
[i,j]
[i,j]的和为
k
k
k的倍数,定义
n
u
m
i
num_i
numi为
s
u
m
j
=
i
(
j
∈
[
1
,
n
]
)
sum_j=i(j\in [1,n])
sumj=i(j∈[1,n])的
j
j
j的个数,则一段序列中,和为
k
k
k的倍数的区间的个数显然为:
∑
i
=
0
k
−
1
C
n
u
m
i
2
\sum_{i=0}^{k-1}C_{num_i}^2
i=0∑k−1Cnumi2
设
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k表示已使用区间和为
[
0
,
i
]
[0,i]
[0,i],占了
j
j
j个位置,优美度为
k
k
k的个数,
s
s
s为区间和为
i
i
i所占的位置数,则
d
p
i
,
j
,
k
dp_{i,j,k}
dpi,j,k要加上
s
s
s从
0
0
0到
j
j
j,
d
p
i
−
1
,
j
−
s
,
k
−
C
s
2
dp_{i-1,j-s,k-C_s^2}
dpi−1,j−s,k−Cs2与这
s
s
s个位置的方案数
C
n
−
j
+
s
s
C_{n-j+s}^s
Cn−j+ss的积。即:
d
p
i
,
j
,
k
=
∑
s
=
0
j
d
p
i
−
1
,
j
−
s
,
k
−
C
s
2
×
C
n
−
j
+
s
s
dp_{i,j,k}=\sum_{s=0}^jdp_{i-1,j-s,k-C_s^2}\times C_{n-j+s}^s
dpi,j,k=s=0∑jdpi−1,j−s,k−Cs2×Cn−j+ss
答案即为:
d
p
k
−
1
,
n
,
t
dp_{k-1,n,t}
dpk−1,n,t
代码实现
时间复杂度为 O ( N 5 ) O(N^5) O(N5),勉强过。空间复杂度可能过大,考虑到一次转移只与 i − 1 i-1 i−1有关,可以用滚动数组。或考虑到 j j j是从小转移到大,可以先从 d p i , n , k dp_{i,n,k} dpi,n,k开始转移。
有dalao写了极其fast的代码,可以去看看。
数据卡常,注意常数优化
#include<bits/stdc++.h>
using namespace std;
const int N=66,mod=998244353;
int n,k,t,dp[N+1][N*(N+1)/2],fac[N+1][N+1],K;
int add(int a,int b){
if(a+b>=mod)return a+b-mod;
return a+b;
}
void init(){
fac[0][0]=1;
for(int i=1;i<N;i++)
{
fac[i][0]=1;
for(int j=1;j<=i;j++){
fac[i][j]=add(fac[i-1][j],fac[i-1][j-1]);
}
}
}
int C(int x,int y){
if(x<y)return 0;
return fac[x][y];
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>K>>t;
init();
for(int y=0;y<=n;++y){
dp[y][C(y+1,2)]=C(n,y);
}
for(int i=1;i<K;i++){
for(int j=n;j>=0;j--){
for(int k=t;k>=0;k--){
for(int s=1;s<=j&&k-C(s,2)>=0;s++){
dp[j][k]=add(dp[j][k],1ll*dp[j-s][k-C(s,2)]*C(n-j+s,s)%mod);
}
}
}
}
cout<<dp[n][t];
}