题目描述
难度分:2024
输入n(2≤n≤200000)以及有n个数位的数字s,保证s不含0。
把s分割成若干段,得分为每一段的乘积。特别地,如果不分割,则得分为s。
输出所有分割方案的得分之和,模998244353。
注:一共有2n−1种分割方案。
输入样例1
3
234
输出样例1
418
输入样例2
4
5915
输出样例2
17800
输入样例3
9
998244353
输出样例3
258280134
算法
动态规划
这个题肯定需要计算每个划分出来的段对答案的贡献,不然枚举所有的2n−1种划分方案必然会超时。
状态定义
dp[i]表示划分s[1:i]的所有划分方案得分之和。
状态转移
枚举前一个分割点j,那么s[j+1:i]就能划分出一个数字,状态转移方程为
dp[i]=Σj=i−1j=0dp[j]×s[j+1:i]
但是这样枚举的话时间复杂度为O(n2)会超时,因此可以将这个式子变形。
dp[i+1]
=Σj=ij=0dp[j]×s[j+1:i+1]
=Σj=ij=0dp[j]×(s[j+1:i]×10+s[i+1])
=10Σj=ij=0dp[j]×s[j+1:i]+Σij=0dp[j]×s[i+1]
由于j=i时,s[j+1:i]不存在,所以Σj=ij=0dp[j]×s[j+1:i]=Σj=i−1j=0dp[j]×s[j+1:i],于是有如下状态转移方程。
dp[i+1]=10dp[i]+Σij=0dp[j]×s[i+1]
这样一来就可以一边递推一边维护个dp数组的前缀和来O(1)地求第二项。
复杂度分析
时间复杂度
只有从1遍历到n一重循环,因此时间复杂度为O(n),是线性的。
空间复杂度
开辟了一个dp数组,与s同规模,额外空间复杂度为O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 200010, MOD = 998244353;
LL dp[N];
int n;
char s[N];
int main() {
scanf("%d", &n);
scanf("%s", s + 1);
dp[0] = 0;
LL presum = 1;
for(int i = 1; i <= n; i++) {
int d = s[i] - '0';
dp[i] = (10ll*dp[i - 1]%MOD + presum*d%MOD)%MOD;
presum = (presum + dp[i]) % MOD;
}
printf("%d\n", dp[n]);
return 0;
}