忘掉过去的人,终将重蹈覆辙。|

chenwenmo

园龄:6个月粉丝:0关注:7

2024-10-11 17:05阅读: 30评论: 0推荐: 0

题解:ABC292G Count Strictly Increasing Sequences

ABC292G Count Strictly Increasing Sequences

题目描述

你有 n 个数,每个数长度为 m

不过这 n 个数中,可能有某些位不确定,需要你在每个 ? 位置上 09 之间填一个数。设你填出来的序列是 {Si}

请你求出,在所有可能的填数方案中,有多少种满足 S1<S2<<Sn?对 998244353 取模。允许前导零存在。

n,m40

思路

推导性质:

我们考虑一个合法的序列长什么样,它的每个数的最高位可能是这样的,

111...222...333...444......999,

其中,最高位的数严格递增,即可保证序列严格递增,那如果最高位相等,就要求往后一位(去掉最高位后)严格递增。

状态设计:

f(i,j,l,r) 表示,当前考虑了后 i 位(越靠前的位权越高)(im 这一维的),[l,r] (是 n 这一维的) 这个区间的数严格递增,且当前填的最大的数是 j,的方案数。

举个例子:12345123561237812390f(2,9,1,4) 就表示 [1,4] 的数,45<56<78<90,且最大的数是 max{4,5,7,9}

转移:

我们枚举一个 k,表示 [l,k] 的数严格递增,[k+1,r] 的数最高位相等,

转移即是:f(i,j,l,r)=f(i,j1,l,k)×f(i+1,9,k+1,r)

含义就是,既然 [l,k] 的数严格递增,于是我们就直接算上它的贡献,然后再保证 [k+1,r] 严格递增,保证的方法就是让它上一位严格递增。

然后 j 这一维是可以用滚动数组滚掉的。

时间:O(n3mV),空间:O(n3)

代码

const int N = 40 + 5, mod = 998244353;

int n, m;
string s[N];
int f[N][N][N], g[N][N][N]; // 滚动数组

void Solve(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> s[i];
		s[i] = " " + s[i];
	}
	for(int i = 1; i <= n; i++) f[m + 1][i][i] = 1; // 边界
	for(int i = m; i >= 1; i--){
		for(int j = 0; j <= 9; j++){
			memcpy(g[i], f[i], sizeof(f[i]));
			for(int len = 1; len <= n; len++){
				for(int l = 1; l + len - 1 <= n; l++){
					int r = l + len - 1;
					bool flag = true;
					for(int k = r; k >= l; k--){
						if(s[k][i] == '?' || s[k][i] == j + '0'){
							if(k == l) break;
							f[i][l][r] = (1ll * f[i][l][r] + 1ll * g[i][l][k - 1] * f[i + 1][k][r] % mod) % mod;
						}else{
							flag = false;
							break;
						}
					} // 最后需要特判整个区间都相等
					if(flag) f[i][l][r] = (1ll * f[i][l][r] + f[i + 1][l][r]) % mod;
				}
			}
		}
	}
	cout << f[1][1][n] << endl;
}

本文作者:chenwenmo

本文链接:https://www.cnblogs.com/chenwenmo/articles/18458878

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   chenwenmo  阅读(30)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起