【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;
}
相关文章
相关标签/搜索