题目:
Fish 是一条生活在海里的鱼,有一天他很无聊,就开始数数玩。他数数玩的具体规则是:
-
确定数数的进制$B$
-
确定一个数数的区间$[L, R]$
-
对于$[L, R] $间的每一个数,把该数视为一个字符串,列出该字符串的每一个(连续的)子串对应的$B$进制数的值。
-
对所有列出的数求和。现在Fish 数了一遍数,但是不确定自己的结果是否正确了。由于$[L, R] $较大,他没有多余精力去验证是否正确,你能写一个程序来帮他验证吗?
非常恶心的一道数位$DP$
首先是数位$DP$的常规套路,用$[1,R]$的答案减去$[1,L-1]$的答案
对于一个$B$进制数$S$,令$f_{S}$表示$S$所有后缀串所表示数的和,$l_{S}$表示数$S$的位数,现在在它末尾填上一个数$x$,则$F_{Sx}=F_{S}*B+x*(l_{S}+1)$
令$g_{S}$表示$S$所有子串所表示数的和,则$g_{Sx}=g_{S}*B+F_{Sx}$
我们要对$[1,S]$里的所有数进行统计,令$F_{i,0}$表示从高到低遍历到了第i位,未达到上限的所有数的$f_{x}$,$F_{i,1}$是达到上限的
可得$F_{i+1,0}=sum_{x=1}^{B}(F_{i,0}*B+x*sum (l_{S}+1))=B^{2}F_{i,0}+frac{B(B-1)}{2}sum (l_{S}+1)$
而$F_{i,1}$转移到$F_{i+1,0}$的也是类似的
显然我们还要维护一个数组$L_{i}$,表示前i位数中出现的数的$l_{x}$之和
因为要加上$sum (l_{S}+1)$,还需要维护一个$Sum_{i}$,表示前i位数中出现的数的数量
这两个数组都很好维护
最后就是统计答案了,令$G_{i,0}$表示从高到低遍历到了第i位,未达到上限的所有数的$g_{x}$之和,$G_{i,1}$是达到上限的
因为每个$G_{i,0}$都有$B$次被转移,所以$G_{i+1,0}=Bcdot G_{i,0}+F_{i+1,0}$
而$G_{i,1}$转移到$G_{i+1,0}$的情况也是类似的
注意前导零的处理,我的方法是每遍历到新的一位,1~B每个数都还会作为一个新数的开头(除了第一位),把都加入状态里即可
1 #include <cmath> 2 #include <queue> 3 #include <vector> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #define N1 101000 8 #define N2 4201 9 #define M1 120 10 #define ll long long 11 #define dd double 12 #define uint unsigned int 13 #define idx(X) (X-'0') 14 using namespace std; 15 16 const int mod=20130427; 17 int gint() 18 { 19 int ret=0,fh=1;char c=getchar(); 20 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 21 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 22 return ret*fh; 23 } 24 int n,m,B; 25 int f[N1][2],g[N1][2],s[N1][2],l[N1][2]; 26 ll solve(int *a,int len) 27 { 28 memset(f,0,sizeof(f)); 29 memset(g,0,sizeof(g)); 30 memset(l,0,sizeof(l)); 31 memset(s,0,sizeof(s)); 32 ll ans=0; 33 s[1][0]=a[1]-1,s[1][1]=1; 34 l[1][0]=a[1]-1,l[1][1]=1; 35 f[1][0]=1ll*a[1]*(a[1]-1)/2%mod; 36 f[1][1]=a[1]; 37 g[1][0]=f[1][0],g[1][1]=f[1][1]; 38 for(int i=1;i<len;i++) 39 { 40 s[i+1][0]=(1ll*s[i][0]*B%mod + 1ll*s[i][1]*a[i+1]%mod + B-1)%mod; 41 s[i+1][1]=s[i][1]; 42 l[i+1][0]=(1ll*(l[i][0]+s[i][0])*B%mod + 1ll*(l[i][1]+s[i][1])*a[i+1]%mod + B-1)%mod; 43 l[i+1][1]=(l[i][1]+s[i][1]); 44 f[i+1][0]=(1ll*f[i][0]*B%mod*B%mod + 1ll*f[i][1]*a[i+1]%mod*B%mod + 1ll*(1ll*B*(B-1)/2%mod)*(l[i][0]+s[i][0]+1)%mod + 1ll*(1ll*a[i+1]*(a[i+1]-1)/2%mod)*(l[i][1]+s[i][1])%mod)%mod; 45 f[i+1][1]=(1ll*f[i][1]*B%mod + 1ll*a[i+1]*(l[i][1]+1)%mod)%mod; 46 g[i+1][0]=(1ll*g[i][0]*B%mod + 1ll*g[i][1]*a[i+1]%mod + f[i+1][0])%mod; 47 g[i+1][1]=(g[i][1] + f[i+1][1])%mod; 48 } 49 return (g[len][0]+g[len][1])%mod; 50 } 51 int a[N1],b[N1],tmp[N1]; 52 53 54 int main() 55 { 56 scanf("%d",&B); 57 scanf("%d",&n); 58 for(int i=n;i>=1;i--) 59 tmp[i]=gint(); 60 scanf("%d",&m); 61 for(int i=1;i<=m;i++) 62 b[i]=gint(); 63 tmp[1]--;int k=1; 64 while(tmp[k]<0) 65 tmp[k]+=B,tmp[k+1]--,k++; 66 if(tmp[n]==0) n--; 67 for(int i=1;i<=n;i++) 68 a[i]=tmp[n-i+1]; 69 ll ans1=solve(a,n); 70 ll ans2=solve(b,m); 71 printf("%lld ",((ans2-ans1)%mod+mod)%mod); 72 return 0; 73 }