又是一个计数题,,
在n个数中间填加号,求所有方案的数字和。
下面提供两种解法,题解的和来自FSY的。
题解:
考虑每个区间对数的贡献,要么没有贡献,要么贡献是10的幂。
故我们可以枚举使得这个数的系数为10^i时,区间的个数。发现是个组合数。
发现这样枚举实际上是固定了该点所在区间的右端点,也就是说固定了一个加号的位置。设当前点为i,对i的贡献为10^j,则右端点为i+j。
发现i~i+j不能填加号,共j个位置。而i+j右边强制加了一个加号。所以剩下的加号方案为
故贡献为
发现复杂度n^2,考虑交换枚举顺序。如果先枚举j,即先枚举区间右端点到点i的距离。
于是等差数列求和即可。
方法2:
考虑枚举区间,计算区间被计算的次数。
在不考虑1和n两个端点的情况下,考虑一个区间[l,r]。
使得这个区间存在的条件是区间左右各有一个加号,区间内部没有加号。
所以加号的组合方案为。故贡献为
发现复杂度为n^2,发现组合数只跟区间长度len有关,考虑如何计算长度为len的所有区间和。
举例子时间:
如果有样例abcdefg
发现长度为1的和是a,b,c,d,e,f,g
长度为2的是ab,bc,cd,de,ef,fg
也就是10*(a+b+c+d+e+f)+(b+c+d+e+f+g)。
多举几个例子,你会发现,对于一个长度len,其答案为(len-1)的答案去掉最后一项*10,然后加上一个个位的后缀和。
所以维护后缀和然后该怎么计算怎么计算。
对于区间端点为1或者n的,自己手玩一下该怎么算,区别不大。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;ch=getchar();
}return cnt*f;
}
int fac[1000003],ifac[1000003];
int sum[1000003];
int suml[1000003];
int n,m;char ch[1000003];
const int mod=998244353;
int ksm(int a,int b){
int gu=1;
while(b){
if(b&1)gu=gu*a%mod;a=a*a%mod;b>>=1;
}return gu;
}
int jz[1000003];
int sumsuf[1000003];
int tem;
signed main(){
n=in;m=in;fac[0]=ifac[0]=1;jz[0]=1;
scanf("%s",ch+1);for(int i=1;i<=n;i++)jz[i]=jz[i-1]*10%mod;
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=1;i--)ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++)sum[i]=(sum[i-1]*10+ch[i]-'0')%mod;
for(int i=n-1;i>=1;i--)sumsuf[i]=(sumsuf[i+1]+ch[i]-'0')%mod;
for(int i=2;i<=n-1;i++)suml[1]+=ch[i]-'0',suml[1]%=mod;
tem=suml[1]-ch[n-1]+'0';
for(int i=2;i<n;i++){
suml[i]=10*tem%mod+sumsuf[i+1];suml[i]%=mod;
tem=suml[i]-(sum[n-1]-sum[n-i-1]*jz[i]%mod)%mod;tem=(tem%mod+mod)%mod;
}`
// for(int i=1;i<=n;i++){
// cout<<suml[i]<<" ";
// }cout<<endl;
int ans=0;
for(int i=1;i<=n;i++){
int x=m-2,y=n-i-2;
if(x>y||x<0||y<0)continue;
// cout<<x<<" "<<y<<" "<<suml[i]<<" "<<fac[y]<<" "<<ifac[x]<<" "<<ifac[y-x]<<endl;
ans=(ans+suml[i]*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
//c/out<<ans<<endl;
}//cout<<ans<<endl;
for(int i=1;i<n;i++){
int x=m-1,y=n-1-i;if(x<0||y<0| |y<x)continue;
ans=(ans+sum[i]*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
}
//cout<<ans<<endl;
for(int i=2;i<=n;i++){
int x=m-1,y=i-2;if(x<0||y<0||y<x)continue;
// cout<<i<<" "<<sum[n]<<" "<<sum[i-1]<<" "<<jz[n-i+1]<<" "<<sum[n]-sum[i-1]*jz[n-i+1]<<endl;
ans=(ans+(sum[n]-sum[i-1]*jz[n-i+1]%mod+mod)%mod*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
}
// ans=(ans+sum[n]*(m==0)+mod)%mod;
cout<<ans;
return 0;
}