原题题面:https://ac.nowcoder.com/acm/contest/33192/J
题目大意:已知 k ( 0 < k < 65 ) k(0<k<65) k(0<k<65),对于每个仅包含 [ 0 , k − 1 ] [0,k-1] [0,k−1]区间内的整数的数组,定义其优美度为非空连续子数组之和为 k k k的倍数的数量。求有多少长度为 n n n的数组,其优美度为 t t t ,答案对 998224353 998 224 353 998224353取模
题目分析:
因为是求连续子数组之和,我们应该会情不自禁地想起前缀和
s
u
m
i
=
s
u
m
i
−
1
+
a
i
sum_i=sum_{i-1}+a_i
sumi=sumi−1+ai
因为题目只需知道其是否为
k
k
k的倍数,并不关心其值,所以不妨改动一下:
s
u
m
i
=
(
s
u
m
i
−
1
+
a
i
)
sum_i=(sum_{i-1}+a_i)
sumi=(sumi−1+ai) %
k
k
k
显然,只要保证 ( s u m y − s u m x ) = 0 ,即( s u m x = s u m y ) (sum_y-sum_x)=0,即 (sum_x=sum_y) (sumy−sumx)=0,即(sumx=sumy),就可说明在 [ x + 1 , y ] [x+1,y] [x+1,y]内的值的和为 k k k的倍数
为了统计,不妨设 n u m b e r i 为有多少 s u m x = i number_i为有多少sum_x=i numberi为有多少sumx=i
两两搭配为 k k k的倍数的方案数为 C n u m b e r i 2 C^2_{number_i} Cnumberi2
以上便是做这道题的基础
正题:
读完题目,应该敏锐地想到动态规划
不妨设 d p x , y , z dp_{x,y,z} dpx,y,z表示使用 [ 0 , x ] [0,x] [0,x]区间内的数,填充了 y y y个位置,优美度为 z z z的方案数
枚举用
i
:
s
i:s
i:s次时的贡献,所以可得
d
p
x
,
y
,
z
=
∑
s
=
0
y
d
p
x
−
1
,
j
−
s
,
z
−
C
s
2
∗
C
y
s
dp_{x,y,z}=\sum^y_{s=0}dp_{x-1,j-s,z-C^2_s}*C^s_y
dpx,y,z=s=0∑ydpx−1,j−s,z−Cs2∗Cys
总结:
以上代码总时间复杂度为
O
(
2
∗
n
+
l
o
g
n
+
n
5
)
≈
2
30
+
200
O(2*n+log_n+n^5) \approx 2^{30}+200
O(2∗n+logn+n5)≈230+200 勉强可通过
空间复杂度为
O
(
n
4
)
O(n^4)
O(n4)
Tip: 这道题时间很吃紧,代码要比较优秀,补题时超时了好几次
代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int MXN=70;
const LL mod=998244353;
LL fac[MXN],ifac[MXN];
LL dp[MXN][MXN][MXN*MXN];
LL c[MXN][MXN];
LL fast(LL x,LL y){
LL res=1,tmp=x;
while(y){
if(y&1) res=res*tmp%mod;
tmp=tmp*tmp%mod;
y>>=1;
}
return res;
}
void Init(){//预处理阶乘
fac[0]=1;
for(int i=1;i<=66;i++){
fac[i]=fac[i-1]*(LL)i%mod;
}
ifac[66]=fast(fac[66],mod-2);
for(int i=65;i>=0;i--){
ifac[i]=ifac[i+1]*(LL)(i+1)%mod;
}
}
LL C(int n,int m){//求组合数
if(n<m) return 0;
if(n==m) return 1;
if(c[n][m]) return c[n][m]; //记忆化,不然会超时
return c[n][m]=((fac[n]*ifac[m])%mod*ifac[n-m])%mod;
}
int main(){
int n,k,t,i,j,d,s;
LL tmpp;
Init();
scanf("%d%d%d",&n,&k,&t);
for(i=1;i<=n;i++){
dp[0][i][i*(i+1)/2]=1;
}
for(i=0;i<k;i++){
dp[i][1][0]=i; dp[i][1][1]=1; dp[i][0][0]=1;
}
for(i=1;i<k;i++){
for(j=2;j<=n;j++){
for(d=0;d<=min(t,j*(j+1)/2);d++){
for(s=0;s<=j;s++){
tmpp=C(s,2);
if(tmpp>d) continue;
dp[i][j][d]=(dp[i][j][d]+dp[i-1][j-s][d-tmpp]*C(j,s)%mod)%mod;
}
}
}
}
printf("%lld\n",dp[k-1][n][t]);
}