“蔚来杯“2022牛客暑期多校训练营7 J.Melborp Elcissalc

原题链接

传送

题目大意

已知 k ( 1 ≤ k ≤ 64 ) k(1 \leq k \leq 64) k(1k64) ,对于每个仅包含 [ 0 , k − 1 ] [0,k-1] [0,k1] 区间内的整数的数组,定义其优美度为非空连续子数组之和为 k k k 的倍数的数量。
求有多少长度为 n n n 的数组,其优美度为 t t t ,答案对 998244353 998244353 998244353 取模。

题解

对于快速求区间和,采用前缀和算法,
由于要求 k k k 的倍数,所以我们可以把前缀和 m o d   k mod\ k mod k 容易想到,由取模而得到的前缀和是单一的,并且可以通过倒推得到。
如何由前缀和快速判断是否优美
设该数组的前缀和为 s u m sum sum ,则其区间和为 s u m r − s u m l − 1 sum_r-sum_{l-1} sumrsuml1,由取模操作可以得到,当 s u m l − 1 = = s u m r sum_{l-1}==sum_r suml1==sumr 时该区间是优美的。所以不难由前缀和,通过组合数学算出优美度。设前缀和为 i i i 的有 d i d_i di 个,所以由一个已知数组可以得到的优美度为 ∑ C d i 2 \sum C_{d_i}^2 Cdi2
如何快速构造符合的数组
由于前缀和和数组一一对应,考虑构造满足的前缀和,可以设 D P DP DP 解决,
d p i , j , k dp_{i,j,k} dpi,j,k 为仅使用 i i i 个数字内占用了 j j j 个位置,其优美度为 k k k 的数组数量。
考虑转移
每添加一个新的数字的可能数,在 d p i , j , k dp_{i,j,k} dpi,j,k 中,设加入的数字数为 x x x ,剩余的空位显然为 n − y n-y ny,所以转移式为 d p i + 1 , j + x , k + C x 2 + = d p i , j , k ∗ C n − y x ( 1 ≤ x ≤ n − y ) dp_{i+1,j+x,k+C_{x}^2}+=dp_{i,j,k}*C_{n-y}^{x}(1\leq x \leq n-y) dpi+1,j+x,k+Cx2+=dpi,j,kCnyx(1xny)
提前特殊处理初始化 i = 0 i=0 i=0 的情况,
暴力枚举四维即可,复杂度为 O ( 6 4 5 ) = O(64^5)= O(645)= 能过 。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=70;
const int mod=998244353;
int dp[N][N][N*N];
int C[N][N];
int inc[N],fac[N];
int n,t,k;
int ksm(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=1ll*ret*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ret;
}
void init()
{
    for(int i=0;i<N;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()
{
    init();
    scanf("%d%d%d",&n,&k,&t);
    for(int i=0;i<=n;i++)                    //初始化,容易算得i个零的情况可能为C(n,i)
        dp[0][i][C[i+1][2]]=C[n][i];
    for(int i=0;i<k-1;++i)             //转移
    {
        for(int j=0;j<=n;++j)
            for(int l=0;l<=t;++l)
            {
                if(!dp[i][j][l])
                    continue;
                for(int x=0;j+x<=n&&l+C[x][2]<=t;++x)
                    dp[i+1][j+x][l+C[x][2]]=(1ll*dp[i+1][j+x][l+C[x][2]]+dp[i][j][l]*C[n-j][x])%mod;
            } 
    }
    printf("%d",dp[k-1][n][t]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值