【BZOJ3326】數數(SCOI2013)-數位DP

測試地址:數數
題目大意: 給定 L , R L,R 兩個 1 0 5 10^5 位內的 B ( 1 0 5 ) B(\le 10^5) 進制數, L R L\le R ,對區間 [ L , R ] [L,R] 內的全部數 x x ,累加 x x 中全部子串表示的數字的和(如 123 123 ,應該累加 123 + 12 + 23 + 1 + 2 + 3 123+12+23+1+2+3 到答案中,注意不該該包含前導零),求最終答案(用 10 10 進製表示)。
作法: 本題須要用到數位DP。
神題。雖然一眼能看出數位DP,但具體的轉移式子仍是想錯了好多回,此次終於寫對了。
首先咱們來看,對於一個 l e n len 位的數 x x ,在它末尾加一位數 s s ,會對這個數的全部子串表示的數字和有什麼影響(如下簡寫成 s u m ( x ) sum(x) )。令 s u f ( x ) suf(x) 表示數 x x 全部後綴表示的數字和,那麼有:
s u f ( x n e w ) = s u f ( x l a s t ) B + s ( l e n + 1 ) suf(x_{new})=suf(x_{last})\cdot B+s\cdot (len+1)
s u m ( x n e w ) = s u m ( x l a s t ) + s u f ( x n e w ) sum(x_{new})=sum(x_{last})+suf(x_{new})
根據這個爲基礎,咱們就能思考數位DP的轉移了。
根據套路,首先把問題轉化爲:用小於等於 R R 的全部數的貢獻,減去小於等於 L 1 L-1 的全部數的貢獻,因而如今咱們考慮求小於等於某個數 T T 時的貢獻。
從高位向低位枚舉,令 S u m ( i , 0 / 1 ) Sum(i,0/1) 表示前 i i 位中,不卡/卡上界的全部數的 s u m ( x ) sum(x) 的和, S u f ( i , 0 / 1 ) Suf(i,0/1) 表示前 i i 位中,不卡/卡上界的全部數的 s u f ( x ) suf(x) 的和。先分析具體的轉移過程:
i 1 i-1 位的數不卡上界時,第 i i 位能夠填 0 0 ~ B 1 B-1 內全部的數,而且新的數都不卡上界;
i 1 i-1 位的數卡上界時,第 i i 位能夠填 0 0 ~ T [ i ] T[i] 。當填 0 0 ~ T [ i ] 1 T[i]-1 時,新的數不卡上界,當填 T [ i ] T[i] 時新的數卡上界。
因而咱們先考慮 S u f Suf 的轉移。首先,卡上界的狀況應該很好轉移了,實際上就是求上界的 s u f suf 值。主要是不卡上界的狀況比較複雜。
首先考慮不卡上界轉移到不卡上界的狀況。根據上面的轉移式子 s u f ( x n e w ) = s u f ( x l a s t ) B + s ( l e n + 1 ) suf(x_{new})=suf(x_{last})\cdot B+s\cdot (len+1) ,咱們這樣考慮:首先枚舉 s s 0 0 B 1 B-1 ,對於每個 s s ,再枚舉可轉移的 x l a s t x_{last} ,把貢獻累加起來。因而一個 s s 對整個 S u f Suf 的貢獻是: B s u f ( x l a s t ) + s ( l e n ( x l a s t ) + 1 ) B\cdot \sum suf(x_{last})+s\cdot \sum (len(x_{last})+1) ,那麼對於全部 s s ,對 S u f Suf 的貢獻就是: B B s u f ( x l a s t ) + B ( B 1 ) 2 ( l e n ( x l a s t ) + 1 ) B\cdot B\cdot \sum suf(x_{last})+\frac{B(B-1)}{2}\cdot \sum (len(x_{last})+1) 。其中 s u f ( x l a s t ) \sum suf(x_{last}) 就是 S u f ( i 1 , 0 ) Suf(i-1,0) ,而 ( l e n ( x l a s t ) + 1 ) \sum (len(x_{last})+1) 須要斟酌一下。這個和式是在求,對於全部可轉移的 x l a s t x_{last} (包括 0 0 ),累加它們的位數 + 1 +1 (把 0 0 的位數看作 0 0 )。觀察規律,咱們發現: 1 1 1 1 個, 2 2 B 1 B-1 個, 3 3 ( B 1 ) B (B-1)\cdot B 個, 4 4 ( B 1 ) B 2 (B-1)\cdot B^2 個… i 1 i-1 ( B 1 ) B i 3 (B-1)\cdot B^{i-3} 個, i i n u m B i 2 num-B^{i-2} 個, n u m num 表示 T T 的前 i 1 i-1 位組成的前綴。那麼前面的有規律的部分能夠遞推維護,而 n u m num 顯然也能夠遞推維護,因此咱們就能夠每次 O ( 1 ) O(1) 地進行這個轉移了。
接下來考慮卡上界轉移到不卡上界的狀況。這種狀況下,能轉移到不卡上界的狀況, s s 必須是 0 0 ~ T [ i ] 1 T[i]-1 ,並且由於這種狀況中可轉移的 x l a s t x_{last} 只有一個,並且 l e n ( x l a s t ) len(x_{last}) 就是 i 1 i-1 ,所以就比上面的狀況簡單不少了,總貢獻應該爲 S u f ( i 1 , 1 ) B + T [ i ] ( T [ i ] 1 ) 2 i Suf(i-1,1)\cdot B+\frac{T[i](T[i]-1)}{2}\cdot i
那麼 S u f Suf 的轉移討論完了,接下來討論 S u m Sum 的轉移。 S u m ( i , 1 ) Sum(i,1) 就是求上界的 s u m sum ,而 S u m ( i , 0 ) Sum(i,0) 也利用上面的思考方式,先考慮從不卡上界轉移的狀況,由於有 B B 種轉移,因此 S u m ( i 1 , 0 ) Sum(i-1,0) 就產生了 B B 次的貢獻。再考慮從卡上界轉移的狀況,由於有 T [ i ] T[i] 種轉移,因此 S u m ( i 1 , 1 ) Sum(i-1,1) 就產生了 T [ i ] T[i] 次的貢獻。再加上全部不卡上界數的後綴產生的貢獻,即 S u f ( i , 0 ) Suf(i,0) ,就能夠計算出 S u m ( i , 0 ) Sum(i,0) 了。
至此,通過漫長的討論,咱們獲得了一個 O ( n ) O(n) 的數位DP,完美地解決了這一道題。
如下是本人代碼:php

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=20130427;
int n;
ll s[100010],B,sum[100010][2],suf[100010][2];
ll pw[100010],num[100010],sumnum[100010]={0};

ll solve()
{
	sum[0][0]=sum[0][1]=suf[0][0]=suf[0][1]=num[0]=0;
	for(int i=1;i<=n;i++)
	{
		if (i>2) sumnum[i]=(sumnum[i-1]+(ll)(i-1)*(B-1ll)%mod*pw[i-3]%mod)%mod;
		num[i]=(num[i-1]*B%mod+s[i])%mod;
		ll tmp=0;
		if (i>1) tmp=(sumnum[i]+1ll+(num[i-1]-pw[i-2]+mod)%mod*(ll)i%mod)%mod;
		suf[i][1]=(suf[i-1][1]*B%mod+s[i]*(ll)i%mod)%mod;
		suf[i][0]=(suf[i-1][0]*B%mod*B%mod+B*(B-1ll)/2ll%mod*tmp%mod)%mod;
		suf[i][0]=(suf[i][0]+suf[i-1][1]*B%mod*s[i]%mod+s[i]*(s[i]-1ll)/2ll%mod*(ll)i%mod)%mod;
		sum[i][1]=(sum[i-1][1]+suf[i][1])%mod;
		sum[i][0]=(sum[i-1][0]*B%mod+suf[i][0])%mod;
		sum[i][0]=(sum[i][0]+sum[i-1][1]*s[i]%mod)%mod;
	}
	return (sum[n][0]+sum[n][1])%mod;
}

int main()
{
	ll ans;
	
	scanf("%lld",&B);
	scanf("%d",&n);
	pw[0]=1;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		pw[i]=pw[i-1]*B%mod;
	}
	ans=(mod-solve()+sum[n][1])%mod;
	
	scanf("%d",&n);
	pw[0]=1;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		pw[i]=pw[i-1]*B%mod;
	}
	ans=(ans+solve())%mod;
	
	printf("%lld",ans);
	
	return 0;
}
相關文章
相關標籤/搜索