BZOJ 3326 [SCOI2013]数数 (数位DP)

洛谷传送门

题目:

Fish 是一条生活在海里的鱼,有一天他很无聊,就开始数数玩。他数数玩的具体规则是:

  1. 确定数数的进制$B$

  2. 确定一个数数的区间$[L, R]$

  3. 对于$[L, R] $间的每一个数,把该数视为一个字符串,列出该字符串的每一个(连续的)子串对应的$B$进制数的值。

  4. 对所有列出的数求和。现在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 }
原文地址:https://www.cnblogs.com/guapisolo/p/10043806.html