其他分享
首页 > 其他分享> > J-Melborp Elcissalc

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