刷了這麼久的數位 dp ,照樣被這題虐,還從早上虐到晚上,對本身無語...(機房裏又是隻有我一我的,寂寞。)ios
Fish 是一條生活在海里的魚,有一天他很無聊,就開始數數玩。他數數玩的具體規則是:git
肯定數數的進制B數組
肯定一個數數的區間[L, R]atom
對於[L, R] 間的每個數,把該數視爲一個字符串,列出該字符串的每個(連續的)子串對應的B進制數的值。spa
對全部列出的數求和。如今Fish 數了一遍數,可是不肯定本身的結果是否正確了。因爲[L, R] 較大,他沒有多餘精力去驗證是否正確,你能寫一個程序來幫他驗證嗎?code
輸入包含三行。blog
第一行僅有一個數B,表示數數的進制。字符串
第二行有N +1 個數,第一個數爲N,表示數L 在B 進制下的長度爲N,接下里的N個數從高位到低位的表示數L 的具體每一位。get
第三行有M+ 1 個數,第一個數爲M,表示數R 在B 進制下的長度爲M,接下里的M個數從高位到低位的表示數R 的具體每一位。string
輸出僅一行,即按照Fish 數數規則的結果,結果用10 進製表示,因爲該數可能很大,輸出該數模上20130427的模數。
數位 dp ,有點強大,又是一道須要感性理解的題目。。。
首先咱們準備好 dp 狀態: dp[i][2] ,表示共有 i 位的 B 進制數,前綴子串的和。
dp[i][0] 表示無限制(甚至容許前導 0 的存在),
dp[i][1] 表示當前最高位不超過 d[i] (d[i] 表示讀入的 B 進制數從後往前數的第 i 位)
那麼 咱們考慮 dp[i][0] 的轉移:
假設當前 dp[i-1][0] 已經獲得,那麼 處理 dp[i][0] 就至關於 在 dp[i-1][0] 的基礎上,在最高位(即第 i-1 位以前)加上一位 B 進制數 x 。
那麼對於前綴子串的和來講,x 的貢獻也就是讓 後面的 i-1 個數字多了 x 中選擇(0 ~ x-1),而 x 自己對於前綴子串和的貢獻就是 x * B0 + x * B1 +.....+ x*Bi-1 (看不懂能夠多想幾遍,注意抓住貢獻這個關鍵)
那麼 咱們就能夠列出轉移式子了:$$ dp[i][0] = B × dp[i−1][0]+ \drac{B(B−1)}{2} × B[i] × S[i−1] (B[i] = B^{i} , S[i] = \sum_{1}^{i} B[i]) $$
而對於有限制的 dp[i][1] 咱們也能夠對應的獲得式子: $$ dp[i][1] = d[i] × dp[i−1][0]+\drac{d[i](d[i]−1)}{2} × B[i-1] × S[i-1] + dp[i−1][1] + d[i]∗(sub[i−1]+1) × S[i-1] ( d 的意義同上,sub[i] 表示讀入的 B 進制數的長度爲 i 的後綴子串對應值) $$
那麼 對於 ans 的累加就是: $$ ans=\sum_{i=1}^{len} max(0,pre[i+1]−1) × f[i][0]+f[i][1] $$ 也就是說咱們一步一步去處理 dp 數組,而後就能夠同時累加答案了
爲何 ans 這樣累加? 其實上面的式子就是在說,除去當前處理完的 i 位,剩下的(前綴子串對應值)有 pre[i+1] 種取法(由於可含前導零,因此方案數就是前綴子串對應數值)
可是前綴不能取到徹底狀態,因而 -1 。而後加上有限制的 dp[i][1] ,就是當前可累加的答案了
emmmm...麻煩死了...其實不是很難理解,可是很難想到能夠這麼轉移...之類的(都是藉口)
因而...就能夠上代碼了吧?
1 //by Judge 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #define ll long long 6 #define int long long 7 using namespace std; 8 const int M=1e5+111; 9 const ll mod=20130427; 10 //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 11 char buf[1<<21],*p1=buf,*p2=buf; 12 inline int read(){ 13 int x=0,f=1; char c=getchar(); 14 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 15 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 16 } 17 ll B,len,ans,d[M],f[M]={1},sum[M]={1},pre[M],sub[M],dp[M][2]; 18 inline void prep(){ //預處理 19 for(int i=1,j;i<=1e5;++i) 20 f[i]=f[i-1]*B%mod, 21 sum[i]=(sum[i-1]+f[i])%mod; } 22 inline ll solv(){ 23 ll ans=pre[len+1]=0; //噁心的pre初始化,最後的坑 24 for(int i=1;i<=len;++i) sub[i]=(d[i]*f[i-1]%mod+sub[i-1])%mod; 25 for(int i=len;i>=1;--i) pre[i]=(pre[i+1]*B%mod+d[i])%mod; 26 //前綴值、後綴值的預處理 27 for(int i=1;i<=len;++i){ //dp 轉移,查了半天發現沒毛病 28 dp[i][0]=(dp[i-1][0]*B%mod+B*(B-1)/2%mod*f[i-1]%mod*sum[i-1]%mod)%mod; 29 dp[i][1]=(dp[i-1][0]*d[i]%mod+d[i]*(d[i]-1)/2%mod*f[i-1]%mod*sum[i-1]%mod)%mod; 30 dp[i][1]=(dp[i][1]+dp[i-1][1]+d[i]*(sub[i-1]+1)%mod*sum[i-1]%mod)%mod; 31 ans=(ans+max(0ll,pre[i+1]-1)*dp[i][0]%mod+dp[i][1])%mod; 32 } return ans; 33 } 34 signed main(){ 35 B=read(),len=read(),prep(); 36 for(int i=len;i;--i) d[i]=read(); 37 for(int i=1;i<=len;++i) //繁雜的數字處理,坑 38 if(d[i]){ --d[i]; break; } 39 else d[i]=B-1; 40 if(!d[len]) --len; 41 ans=-solv(), len=read(); 42 for(int i=len;i;--i) d[i]=read(); 43 ans+=solv(),printf("%lld\n",(ans%mod+mod)%mod); return 0; 44 }