ABC288F Integer Division

ABC288F Integer Division

题目大意

给你一个有 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,i1)=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=0i1fj×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=0i1fj×sum(j+1,i1)×10)+(sum(i,i)×j=0i1fj)

我们可以发现, ∑ 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=0i1fj×sum(j+1,i1)=j=0i2fj×sum(j+1,i1)=fi1,于是

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×fi1+sum(i,i)×j=0i1fj

可以用前缀和来求 ∑ j = 0 i − 1 f j \sum\limits_{j=0}^{i-1}f_j j=0i1fj

注意因为我们令 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值