【JZOJ4799】我的快乐时代
Description
原题为51Nod卷积和
Solution
设第一天到第
i
天的答案为
然后我们现在要解决 Solve(n) 。
设
n
是个
假设m=5,我们可以先快速求出1~9,10~99,100~999,1000~9999的卷积和。
那么怎么计算呢?
我们知道,1~9就是1 2 +2 2 +…+9 2 =285。
10~99自己算。
对于长度为L(L>2)的,我们假设在开头结尾算,那么卷积和为:
10L−2∑9i=1i∑9i=0i+9(∑9i=0i)210L−3
(想想看为什么)
那么对于长度为m的,例如有一个数 abcde¯¯¯¯¯¯¯¯ 。
我们就分开处理,先处理 10000 到 (a−1)9999¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ,再处理 a0000¯¯¯¯¯¯¯¯¯ 到 a(c−1)999¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ……
至于计算的方法,其实和前面的差不多,只是要固定一些数字。
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define ll long long
#define mo 1000000007
#define N 20
using namespace std;
ll qz[30]={0,1,3,6,10,15,21,28,36,45,0,1,2,3,4,5,6,7,8,9};
ll pf[30]={0,1,5,14,30,55,91,140,204,285,0,1,4,9,16,25,36,49,64,81};
ll z[N];
int q[N];
void get(ll x)
{
q[0]=0;
if(!x) q[0]=1,q[1]=0;
while(x) q[++q[0]]=x%10,x/=10;
}
int fw[N],ct[N];
ll calc(ll n)
{
if(n<1) return 0;
ll ans=0;
get(n);
fo(i,1,q[0]-1)
{
if(i==1)
{
ans=(ans+285)%mo;
continue;
}
else if(i==2)
{
ans=(ans+4050)%mo;
continue;
}
ans=(ans+4050*z[i-2]%mo+4050*9%mo*z[i-3]%mo*(i/2-1)%mo)%mo;
if(i%2==1) ans=(ans+285*9%mo*z[i-2]%mo)%mo;
}
fd(i,q[0],1)
{
ct[i]=10;
fw[i]=9;
}
if(q[0]==1) ans=(ans+pf[q[1]]%mo)%mo;
else if(q[0]==2) ans=(ans+qz[q[2]-1]*45*2%mo+q[2]*qz[q[1]]*2%mo)%mo;
else
fd(i,q[0],1)
{
ct[i]=q[i]-1;
if(i!=q[0]) ct[i]++;
fw[i]=q[i]-1;
if(i==1) fw[i]++,ct[i]++;
int p=q[0]/2;
fo(j,1,p)
{
int k=q[0]-j+1;
ll tmp=0;
if(j==1) tmp=(tmp+qz[fw[j]]*qz[fw[k]]%mo*2%mo)%mo;
else tmp=(tmp+qz[fw[j]]*qz[fw[k]]%mo*2%mo)%mo;
fo(l,1,q[0])
if(l!=j && l!=k) tmp=tmp*ct[l]%mo;
ans=(ans+tmp)%mo;
}
if(q[0]%2==1)
{
p++;
ll tmp=0;
tmp=pf[fw[p]];
fo(j,1,q[0])
if(j!=p) tmp=tmp*ct[j]%mo;
ans=(ans+tmp)%mo;
}
fw[i]=q[i]+10;
ct[i]=1;
}
return ans;
}
int main()
{
z[0]=1;
fo(i,1,N-1) z[i]=z[i-1]*10%mo;
ll l,r;
cin>>l>>r;
cout<<(calc(r)-calc(l-1)+mo)%mo;
}