J-Melborp Elcissalc
作者:互联网
题意
有一个长度为\(n\)的序列,每个位置可以填\(0\)到\(m-1\)之间的一个数,求有多少种构造序列的方式,使得构造出来的序列恰有\(t\)个连续子段满足和可以被\(m\)整除
\(1\leq{n,m}\leq64,0\leq{t}\leq\frac{n(n+1)}{2}\)
题解
本题的关键是要想到区间\([l,r]\)的和能被\(k\)整除,等价于序列的前缀和数组的第\(l-1\)和第\(r\)位在模\(m\)意义下相等(前缀和数组的第0位应该被视作0,不能忽略),又因为序列和它的前缀和数组是一一对应的,所以我们构造的是实际上是前缀和数组
给定一个确定的序列,求序列有多少连续子段和可以被\(k\)整除。
设\(cnt_i\)表示\(i\)在序列的前缀和数组中出现的次数,上述问题的答案显然为\(\sum_{i=0}^nC_{cnt_i}^2\)
基于以上分析,设\(f_{ijk}\)表示填\(0\)到\(i\)之间的数,序列长度为\(j\),有\(k\)个连续子段能被\(m\)整除,枚举序列中\(i\)的数量,容易得到转移方程为
\(f[i][j][k]=\sum_{l=0}^{j}C_j^lf[i-1][j-l][k-C_l^2],k\geq{C_l^2}\)
注意边界的初始值,注意这题组合数比较小,可以预处理存下来,转移时进行适当剪枝,就可以通过极限数据
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=70,mod=998244353;
ll n,m,t,f[N][N][N*N];
ll C[N][N];
void init(){
ll i,j;
C[0][0]=1;
for(i=1;i<=64;++i){
for(j=0;j<=i;++j){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
}
int main(){
init();
ll i,j,k,l;
scanf("%lld%lld%lld",&n,&m,&t);
for(i=1;i<=n;++i){
f[0][i][i*(i+1)/2]=1;
}
for(i=0;i<=m-1;++i){
f[i][1][0]=i,f[i][1][1]=1;
}
for(i=0;i<=m-1;++i){
f[i][0][0]=1;
}
for(i=1;i<=m-1;++i){
for(j=2;j<=n;++j){
for(k=0;k<=min(t,j*(j+1)/2);++k){
for(l=0;l<=j;++l){
if(k<C[l][2]) continue;
f[i][j][k]=(f[i][j][k]+C[j][l]*f[i-1][j-l][k-C[l][2]])%mod;
}
}
}
}
printf("%lld\n",f[m-1][n][t]);
return 0;
}
标签:Elcissalc,前缀,子段,ll,数组,序列,整除,Melborp 来源: https://www.cnblogs.com/DGJG/p/16574498.html