这一道题给我们最大的启示就是一定要学会固定数字!
设\(Pow[i]=B^i\),\(f[i]=\sum_{k=0}^{i}{B^k}\)
\(h[i]\)表示所有\(i\)位数字的所有前缀子串的和
比如\(123456\)一共有\(6\)位,他的所有前缀子串为\(1,12,123,1234,12345,123456\),他的所有前缀子串和就是这六个数加起来,那么\(h[6]\)就表示\(100000,100001,……,999999\)所有这些数字的前缀子串和的和
我们求\(h[i]\)时,考虑最高位填什么。此时最高位在变换(可以从\(0\)一直到\(B\)),而剩下的数字也在变换(可以从\(\underbrace{00...0}_{\text{i-1个0}}\)一直到\(\underbrace{99...9}_{\text{i-1个9}}\)),我们就不好讨论,所以先固定假设我们已经选择了最高位的数字,这个数字为\(j\),剩下位的数字为\(\overline{abc...}\)
那么此时的所以前缀为\(\overline{j},\overline{ja},\overline{jab}...\),他们分别可以表示成\(j,j\cdot Pow[1]+a,j\cdot Pow[2]+a\cdot Pow[1]+b...\),所以这一个已经固定的数字的所有前缀和就是\(j\cdot f[i-1]\)+(\(\overline{abc...}\)这个数字的所有前缀子串和)
此时我们再假设\(j\)固定,\(\overline{abc...}\)在变化,再求一个和(即当最高位为\(j\)时,对\(h[i]\)的贡献),则为\(j\cdot f[i-1]\cdot Pow[i-1]+h[i-1]\)(注意一共有\(Pow[i-1]\)个数)
然后我们再假设\(j\)也在变换,再求一个和,则有$$h[i]=((\sum_{j=0}^{B-1}j)\cdot f[i-1]\cdot Pow[i-1])+B\cdot h[i-1]$$
以上过程完全可以先固定\(\overline{abc...}\),假设\(j\)在变化,然后假设\(\overline{abc...}\)在变化,答案是一模一样的
设\(g[i]\)表示所有\(i\)位数字的所有子串的和,用以上同样的方法可以得出$$g[i]=B\cdot g[i-1]+h[i]$$
然后试填的过程见以下代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
const ll p=20130427;
int n,a[N];
ll f[N],g[N],h[N],Pow[N],B;
void prework()
{
f[0]=1;
Pow[0]=1;
for(int i=1;i<=N-10;i++)
{
Pow[i]=Pow[i-1]*B%p;
f[i]=(f[i-1]+Pow[i])%p;
h[i]=(((B-1)*B>>1)%p*f[i-1]%p*Pow[i-1]%p+B*h[i-1]%p)%p;
g[i]=(B*g[i-1]%p+h[i])%p;
}
}
ll work()
{
ll sum=0,val=0,res=0;
//sum表示已经填了的数字的所有子串的和
//val表示已经填了的数字的所有前缀子串的和
//rse表示结果
for(int i=n;i>=2;i--)
for(int j=1;j<B;j++)
{
res=(res+g[n-i]+h[n-i])%p;
res=(res+(j*Pow[n-i]%p*f[n-i]%p))%p;
}
//这个循环是在填写只有1位,2位...i-1位的情况
//注意不能直接填写最高位的0
//因为此时会导致重复计算
//比如n=3,数字为095
//那么本来应该只算9,5,95这三个子串的
//但如果直接从最高位的0开始算
//就会算成0,9,5,09,95,095
//显然重复了
for(int i=1;i<a[1];i++)
{
res=(res+g[n-1])%p;
res=(res+(i*Pow[n-1]%p*f[n-1]%p+h[n-1])%p)%p;
}
//考虑最高位的写法
val=a[1],sum=a[1];
for(int i=2;i<=n;i++)
{
for(int j=0;j<a[i];j++)
{
res=(res+g[n-i])%p;
ll temp=(val*B%p+(ll)j*i%p)%p;
res=(res+(temp*Pow[n-i]%p*f[n-i]%p+i*h[n-i])%p)%p;
res=(res+sum*Pow[n-i]%p)%p;
}
val=(val*B%p+(ll)a[i]*i%p)%p;
sum=(sum+val)%p;
}
//可以用博客讲的方法推一下上面代码的式子
return (res+sum)%p;
//注意还要在加上sum
//因为要计算这个数本身的贡献
}
int main()
{
scanf("%lld",&B);
prework();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int j=n;
while(!a[j])
{
a[j]=B-1;
j--;
}
a[j]--;
if(j==1&&!a[j])
{
n--;
for(int i=1;i<=n;i++) a[i]=B-1;
}
ll res1=work();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll res2=work();
printf("%lld",(res2-res1+p)%p);
return 0;
}
以上代码肯定会超时,但是这个优化太简单了,具体见洛谷的提交
标签:子串,...,数数,洛谷,前缀,cdot,Pow,overline,3281 From: https://www.cnblogs.com/dingxingdi/p/17800272.html