题目大意
给你一个有 n n n位数的数字 x x x,你可以把 x x x拆成若干段再将每段组成的数字相乘,求所有拆法所得乘积的和。
如 n = 3 , x = 234 n=3,x=234 n=3,x=234
- 拆成1份, 234 = 234 234=234 234=234,贡献为 234 234 234
- 拆成2份, 2 × 34 = 68 2\times 34=68 2×34=68, 23 × 4 = 92 23\times 4=92 23×4=92,贡献为 68 + 92 = 160 68+92=160 68+92=160
- 拆成3份, 2 × 3 × 4 = 24 2\times 3\times 4=24 2×3×4=24
所以答案为 234 + 160 + 24 = 418 234+160+24=418 234+160+24=418
输出答案模 998244353 998244353 998244353后的值。
题解
令 s u m ( i , j ) sum(i,j) sum(i,j)表示第 i i i位到第 j j j位组成的十进制数,令 s u m ( i , i − 1 ) = 0 sum(i,i-1)=0 sum(i,i−1)=0。
设 f i f_{i} fi表示前 i i i位组成的数的经过上述计算的答案。为了方便计算,我们可以令 f 0 = 1 f_0=1 f0=1。那么状态转移式就是
f i = ∑ j = 0 i − 1 f j × s u m ( j + 1 , i ) f_i=\sum\limits_{j=0}^{i-1}f_j\times sum(j+1,i) fi=j=0∑i−1fj×sum(j+1,i)
如果这样求的话,时间复杂度是 O ( n 2 ) O(n^2) O(n2),显然过不了。所以我们可以将式子转化一下,变为
f i = ( ∑ j = 0 i − 1 f j × s u m ( j + 1 , i − 1 ) × 10 ) + ( s u m ( i , i ) × ∑ j = 0 i − 1 f j ) f_i=(\sum\limits_{j=0}^{i-1}f_j\times sum(j+1,i-1)\times 10)+(sum(i,i)\times \sum\limits_{j=0}^{i-1}f_j) fi=(j=0∑i−1fj×sum(j+1,i−1)×10)+(sum(i,i)×j=0∑i−1fj)
我们可以发现, ∑ j = 0 i − 1 f j × s u m ( j + 1 , i − 1 ) = ∑ j = 0 i − 2 f j × s u m ( j + 1 , i − 1 ) = f i − 1 \sum\limits_{j=0}^{i-1}f_j\times sum(j+1,i-1)=\sum\limits_{j=0}^{i-2}f_j\times sum(j+1,i-1)=f_{i-1} j=0∑i−1fj×sum(j+1,i−1)=j=0∑i−2fj×sum(j+1,i−1)=fi−1,于是
f i = 10 × f i − 1 + s u m ( i , i ) × ∑ j = 0 i − 1 f j f_i=10\times f_{i-1}+sum(i,i)\times \sum\limits_{j=0}^{i-1}f_j fi=10×fi−1+sum(i,i)×j=0∑i−1fj
可以用前缀和来求 ∑ j = 0 i − 1 f j \sum\limits_{j=0}^{i-1}f_j j=0∑i−1fj。
注意因为我们令 f 0 = 1 f_0=1 f0=1,所以求 f 1 f_1 f1时不能用上述式子来求,需要单独计算。 f 1 = s u m ( i , i ) f_1=sum(i,i) f1=sum(i,i)。
时间复杂度为 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
using namespace std;
int n;
long long sum,f[200005];
long long mod=998244353;
char s[200005];
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
f[0]=1;
f[1]=s[1]-'0';
sum=f[0]+f[1];
for(int i=2;i<=n;i++){
int t=s[i]-'0';
f[i]=10*f[i-1]+t*sum;
f[i]%=mod;
sum=(sum+f[i])%mod;
}
printf("%lld",f[n]);
return 0;
}