J-Melborp Elcissalc

题意

有一个长度为n的序列,每个位置可以填0m1之间的一个数,求有多少种构造序列的方式,使得构造出来的序列恰有t个连续子段满足和可以被m整除

1n,m64,0tn(n+1)2

题解

本题的关键是要想到区间[l,r]的和能被k整除,等价于序列的前缀和数组的第l1和第r位在模m意义下相等(前缀和数组的第0位应该被视作0,不能忽略),又因为序列和它的前缀和数组是一一对应的,所以我们构造的是实际上是前缀和数组

给定一个确定的序列,求序列有多少连续子段和可以被k整除。

cnti表示i在序列的前缀和数组中出现的次数,上述问题的答案显然为i=0nCcnti2

基于以上分析,设fijk表示填0i之间的数,序列长度为j,有k个连续子段能被m整除,枚举序列中i的数量,容易得到转移方程为

f[i][j][k]=l=0jCjlf[i1][jl][kCl2],kCl2

注意边界的初始值,注意这题组合数比较小,可以预处理存下来,转移时进行适当剪枝,就可以通过极限数据

代码

#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;
}
posted @   DGJG  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示