洛谷:P3281 [SCOI2013]數數 (優秀的解法)

刷了這麼久的數位 dp ,照樣被這題虐,還從早上虐到晚上,對本身無語...(機房裏又是隻有我一我的,寂寞。)ios

 

題目:洛谷P3281 [SCOI2013]數數 

 

題目描述

 

Fish 是一條生活在海里的魚,有一天他很無聊,就開始數數玩。他數數玩的具體規則是:git

  1. 肯定數數的進制B數組

  2. 肯定一個數數的區間[L, R]atom

  3. 對於[L, R] 間的每個數,把該數視爲一個字符串,列出該字符串的每個(連續的)子串對應的B進制數的值。spa

  4. 對全部列出的數求和。如今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× dp[i1][0]+ \drac{B(B1)}{2} × B[i] × S[i1] (B[i] = B^{i} , S[i] = \sum_{1}^{i} B[i]) $$

而對於有限制的 dp[i][1] 咱們也能夠對應的獲得式子: $$ dp[i][1= d[i× dp[i1][0]+\drac{d[i](d[i]1)}{2} × B[i-1] × S[i-1] + dp[i1][1+ d[i](sub[i1]+1) × S[i-1] ( d 的意義同上,sub[i] 表示讀入的 B 進制數的長度爲 i 的後綴子串對應值) $$

那麼 對於 ans 的累加就是: $$ ans=\sum_{i=1}^{lenmax(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 }
相關文章
相關標籤/搜索