BZOJ 3326 [SCOI2013]數數 (數位DP)
題目:
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}+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}=B\cdot 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\n",((ans2-ans1)%mod+mod)%mod); 72 return 0; 73 }
BZOJ 3326 [SCOI2013]數數 (數位DP)