题目来源:https://ac.nowcoder.com/acm/contest/33192/J
题意
已知 k ( 1 ⩽ k ⩽ 64 k(1\leqslant k\leqslant64 k(1⩽k⩽64),对于每个仅包含 [ 0 , k − 1 ] [0,k-1] [0,k−1]区间内整数的数组,定义一个数组的优美度为其所有非空连续子区间中,和为k的倍数的区间数量。
分析
对于本题,首先考虑如何统计一个数组中和为和为k的倍数的区间数量。很容易能够想到使用前缀和,对于前缀和数组中 p r e l pre_l prel和 p r e r pre_r prer,若 ( p r e l − p r e r ) (pre_l-pre_r) (prel−prer) m o d mod mod k = 0 k=0 k=0,则 [ l + 1 , r ] [l+1,r] [l+1,r]区间的和为k的倍数。不妨将前缀和数组中的每个值 m o d mod mod k k k,就能够简化为若 p r e l = p r e r pre_l=pre_r prel=prer,则 [ l + 1 , r ] [l+1,r] [l+1,r]区间的和为k的倍数。由于对于每个前缀和数组,有且仅有一个原数组与之对应,因此我们可以将统计原数组中的和为k的倍数的区间数量,转化为统计前缀和数组中两两相等的元素组数量。
设 s u m i sum_i sumi为前缀和数组中元素 i i i出现次数,则该数组的优美度为: ∑ i = 0 k − 1 C s u m i 2 \sum\limits_{i=0}^{k-1}C_{sum_i}^2 i=0∑k−1Csumi2
我们利用动态规划分别统计每个 s u m i sum_i sumi对优美度的贡献,设 d p i , j , k dp_{i,j,k} dpi,j,k表示已经考虑了 [ 0 , i ] [0,i] [0,i]区间的数,填充了数组中的 j j j个位置,已填充的数对于优美度的贡献为 k k k的数组数量。若取 j j j个数字 i i i填入数组,则其对于优美度的贡献为 C j 2 C_j^2 Cj2,由此我们可以得到转移方程: 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\limits_{s=0}^{j}dp_{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−s)s其中 s s s为已填充的 i i i的数量, C n − ( j − s ) s C_{n-(j-s)}^s Cn−(j−s)s为在未填充的 n − ( j − s ) n-(j-s) n−(j−s)个位置中,在不同位置放入 s s s个 i i i的总方案数。
对于初始化,由于在数组中填入 i i i个 0 0 0对于优美度的贡献为 C i + 1 2 C_{i+1}^2 Ci+12,且这样的数组有 C n i C_n^i Cni个,因此将所有的 d p 0 , i , C i + 1 2 ( 0 ⩽ i ⩽ n ) dp_{0,i,C_{i+1}^2}(0\leqslant i\leqslant n) dp0,i,Ci+12(0⩽i⩽n)赋值为 C n i C^i_n Cni即可。
注意本题时间限制较紧,注意优化常数。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
ll C[70][70];
ll dp[70][70][4900];//使用了[0,i]区间的数,填充了j个位置,优美度为k的数组数量
ll n,k,t;
void Triangle(){
C[0][0]=1;
for(int i=1;i<70;++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(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>k>>t;
Triangle();
for(ll i=0;i<=n;i++)
if(C[i+1][2]<=t)dp[0][i][C[i+1][2]]=C[n][i];
for(ll i=1;i<k;i++)
for(ll j=0;j<=n;j++)
for(ll l=0;l<=t;l++)
for(ll s=0;s<=j;s++)//有几个i
if(l>=C[s][2])
dp[i][j][l]=((dp[i][j][l]+dp[i-1][j-s][l-C[s][2]]*C[n-j+s][s]%mod)>=mod)?((dp[i][j][l]+dp[i-1][j-s][l-C[s][2]]*C[n-j+s][s]%mod)-mod):(dp[i][j][l]+dp[i-1][j-s][l-C[s][2]]*C[n-j+s][s]%mod);
//优化常数,等同于 dp[i][j][l]=dp[i][j][l]+dp[i-1][j-s][l-C[s][2]]*C[n-j+s][s]%mod
cout<<dp[k-1][n][t];
}