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

J题: Melborp Elcissalc

原题链接:https://ac.nowcoder.com/acm/contest/33192/J

题目大意

已知 k ( 1 ≤ k ≤ 64 ) k(1\le k\le 64) k(1k64) ,对于每个仅包含 [ 0 , k − 1 ] [0,k-1] [0,k1] 区间内的整数的数组,定义其优美度为非空子段之和为 k k k 的倍数的数量。

求有多少长度为 n ( 1 ≤ n ≤ 64 ) n(1\le n\le 64) n(1n64) 的数组,其优美度为 t ( 0 ≤ t ≤ n ( n + 1 ) 2 ) t(0\le t\le \frac{n(n+1)}{2}) t(0t2n(n+1)) ,答案对 998244353 998244353 998244353 取模。

题解

先考虑,如何快速求一个数组 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an 的优美度。
求区间和通常的技巧是前缀和,设 p r e 0 = 0 , p r e i = p r e i − 1 + a i pre_0=0,pre_i=pre_{i-1}+a_i pre0=0,prei=prei1+ai ,有前缀和数组 p r e 0 , p r e 1 , . . . p r e n pre_0,pre_1,...pre_n pre0,pre1,...pren
对于一段区间 [ l , r ] [l,r] [l,r] ,其区间和为 p r e r − p r e l − 1 pre_r-pre_{l-1} prerprel1 ,若 p r e r − p r e l − 1 ≡ 0 ( m o d   k ) pre_r-pre_{l-1}\equiv 0(mod\ k) prerprel10(mod k) ,则 [ l , r ] [l,r] [l,r] 提供优美度。
不妨将 p r e i pre_i prei k k k 取模,此时若 p r e l − 1 = p r e r pre_{l-1}=pre_r prel1=prer ,则 [ l , r ] [l,r] [l,r] 提供优美度。
c n t i cnt_i cnti 表示 p r e j = i pre_j=i prej=i 的数量,可得此时数组 a a a 的优美度为:
∑ i = 0 k − 1 C c n t i 2 \sum_{i=0}^{k-1}C_{cnt_i}^2 i=0k1Ccnti2

(模 k k k 意义下)由前缀和数组差分可得到原数组,即前缀和数组与原数组一一对应,不妨对前缀和数组进行计数。

我们可以分别考虑每个 i i i 对应的 c n t i cnt_i cnti 对优美度的贡献,然后用动态规划进行计数。
f x , y , z f_{x,y,z} fx,y,z 表示已经考虑了 i ∈ [ 0 , x ] i\in [0,x] i[0,x] ,且 ∑ i = 0 x c n t i = y \sum\limits_{i=0}^{x}cnt_i=y i=0xcnti=y (已有 y y y 个数字), ∑ i = 0 x C c n t i 2 = z \sum\limits_{i=0}^{x}C_{cnt_i}^2=z i=0xCcnti2=z (当前优美度为 z z z )的数组个数(此时不考虑剩下 n − y n-y ny 个没有数字的空位)
枚举 c n t x cnt_x cntx 的值,可得转移式如下(其中 C n − ( y − i ) i C_{n-(y-i)}^i Cn(yi)i 表示在除去原先的 y − i y-i yi 已有数字的位置后,在剩下 n − ( y − i ) n-(y-i) n(yi) 个位置中插入 i i i x x x 的方案数):
f x , y , z = ∑ i = 0 y f x − 1 , y − i , z − C i 2 ∗ C n − ( y − i ) i f_{x,y,z}=\sum_{i=0}^{y}f_{x-1,y-i,z-C_i^2}*C_{n-(y-i)}^i fx,y,z=i=0yfx1,yi,zCi2Cn(yi)i

初始化时注意,有 y y y 0 0 0 时的优美度为 C y + 1 2 C_{y+1}^2 Cy+12 ,因为前缀和有 p r e 0 = 0 pre_0=0 pre0=0
注意到其中 x x x 维度在转移时只需上一维度,且 y , z y,z y,z 维度都从较小处转移过来,可以用类似背包的倒序枚举的技巧优化掉 x x x 维度。

本题时限较紧,注意常数优化。

参考代码

#include<bits/stdc++.h>
using namespace std;

template<class T>inline void read(T&x){
	char c,last=' ';
	while(!isdigit(c=getchar()))last=c;
	x=c^48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
	if(last=='-')x=-x;
}

const int MAXN=70,P=998244353;
int n,k,t;
int C[MAXN][MAXN];
int f[MAXN][MAXN*MAXN];

inline void add(int&a,int b){//快速取模加法
	a+=b;
	if(a>=P)a-=P;
}

void init(){//组合数打表
	C[0][0]=1;
	for(int i=1;i<MAXN;++i){
		C[i][0]=1;
		for(int j=1;j<=i;++j){
			C[i][j]=C[i-1][j-1];
			add(C[i][j],C[i-1][j]);
		}
	}
}

int main()
{
	read(n),read(k),read(t);
	init();
	for(int y=0;y<=n;++y){
		f[y][C[y+1][2]]=C[n][y];//初始化
	}
	for(int x=1;x<k;++x){
		for(int y=n;y>=0;--y){//倒序枚举
			for(int z=t;z>=0;--z){//倒序枚举
				for(int i=1;i<=n;++i){//枚举数字x的数量
					if(y-i>=0&&z-C[i][2]>=0)add(f[y][z],1ll*f[y-i][z-C[i][2]]*C[n-(y-i)][i]%P);
					else break;//小剪枝
				}
			}
		}
	}
	cout<<f[n][t]<<'\n';
	return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值