洛谷P3281 数位dp

题意:

给出一个进制 B B B,一个 B B B进制下的区间 [ L , R ] [L,R] [L,R],定义 f ( i B ) f(i_{B}) f(iB) i i i的十进制的数 i 10 i_{10} i10的所有子串形成的新数字的总和,例如 f ( 1 0 10 ) = 1 + 0 + 10 = 11 f(10_{10})=1+0+10=11 f(1010)=1+0+10=11,求 ∑ i B = L R f ( i ) \sum_{i_{B}=L}^{R}f(i) iB=LRf(i)

Solution:

一个非常麻烦的数位 d p dp dp,按照数位 d p dp dp的一般套路,都是计算 [ 0 , r ] − [ 0 , l − 1 ] [0,r]-[0,l-1] [0,r][0,l1]

按照数位 d p dp dp的方法,不妨先考虑加入一个数新产生的贡献是多少?

A A A是一个数, B B B是一个数, A B AB AB在下面都表示为首尾拼接的数,例如 A = 10 , B = 35 A=10,B=35 A=10,B=35,则 A B = 1035 AB=1035 AB=1035,并且约定说数位的第几位是从低权位开始数的,比如 1234 1234 1234的第3位是2

f s x f_{sx} fsx为拼接数 s x sx sx的贡献,并且 x x x为单数码数,给 s s s拼接上 x x x,产生的贡献应该是所有新的后缀代表的数的和,新的后缀是在原来后缀的基础上形成的,于是有

f s x = B × f s + x × ( l s + 1 ) f_{sx}=B\times f_{s}+x\times (l_{s}+1) fsx=B×fs+x×(ls+1)

其中 l s l_{s} ls是其中一个 s s s数的长度,按照数位 d p dp dp的思路,在 i i i位放置 x x x,他能产生作用的是前面所能形成的所有数,于是

f s x = B × f s + x × ( t o t l e n s + t o t s ) f_{sx}=B\times f_{s}+x\times (totlen_{s}+tot_{s}) fsx=B×fs+x×(totlens+tots)

其中 t o t l e n s totlen_{s} totlens是可以是 s s s的所有数的长度和, t o t s tot_{s} tots是可以是 s s s的数的后缀数量和,同时按照数位 d p dp dp,我们将是否卡上界分开考虑,设 f [ i ] [ 0 ] f[i][0] f[i][0]是放置了第 i i i位,前 i i i位不卡上界的贡献, f [ i ] [ 1 ] f[i][1] f[i][1]则是卡上界的贡献,接下来的 d p dp dp数组皆是如此,并且 d p dp dp放置数码顺序时从高权位向低权位放置。接下来的 s x sx sx为拼接数方式说明,在 i + 1 i+1 i+1基础上讨论 i i i就等价于 s s s基础上拼接 x x x

于是

f [ i ] [ 0 ] = ∑ x = 0 B − 1 ( B × f [ i + 1 ] [ 0 ] + x × ( t o t l e n [ i + 1 ] [ 0 ] + t o t [ i + 1 ] [ 0 ] + 1 ) ) + ∑ x = 0 n u m s [ i ] − 1 ( B × f [ i + 1 ] [ 1 ] + x × ( t o t l e n [ i + 1 ] [ 1 ] + t o t [ i + 1 ] [ 1 ] ) ) f[i][0]=\sum_{x=0}^{B-1}(B\times f[i+1][0]+x\times(totlen[i+1][0]+tot[i+1][0]+1))+\sum_{x=0}^{nums[i]-1}(B\times f[i+1][1]+x\times (totlen[i+1][1]+tot[i+1][1])) f[i][0]=x=0B1(B×f[i+1][0]+x×(totlen[i+1][0]+tot[i+1][0]+1))+x=0nums[i]1(B×f[i+1][1]+x×(totlen[i+1][1]+tot[i+1][1]))

f [ i ] [ 1 ] = ∑ x = n u m s [ i ] n u m s [ i ] ( B × f [ i + 1 ] [ 1 ] + x × ( t o t l e n [ i + 1 ] [ 1 ] + t o t [ i + 1 ] [ 1 ] ) ) f[i][1]=\sum_{x=nums[i]}^{nums[i]}(B\times f[i+1][1]+x\times (totlen[i+1][1]+tot[i+1][1])) f[i][1]=x=nums[i]nums[i](B×f[i+1][1]+x×(totlen[i+1][1]+tot[i+1][1]))

其中 f [ i ] [ 0 ] f[i][0] f[i][0]的第一个求和中最后一项的最后一项+1的缘故是,需要考虑当前位置放置第一个非0数码,前面放置前导0的情况,此时对于每个 x x x都贡献一个 x x x的量,之后化简可得

f [ i ] [ 0 ] = B 2 × f [ i + 1 ] [ 0 ] + B × ( B − 1 ) 2 × ( t o t l e n [ i + 1 ] [ 0 ] + t o t [ i + 1 ] [ 0 ] + 1 ) + B × n u m s [ i ] × f [ i + 1 ] [ 1 ] + n u m s [ i ] × ( n u m s [ i ] − 1 ) 2 × ( t o t l e n [ i + 1 ] [ 1 ] + t o t [ i + 1 ] [ 1 ] ) f[i][0]=B^{2}\times f[i+1][0]+\frac{B\times(B-1)}{2}\times(totlen[i+1][0]+tot[i+1][0]+1)+B\times nums[i]\times f[i+1][1]+\frac{nums[i]\times(nums[i]-1)}{2}\times(totlen[i+1][1]+tot[i+1][1]) f[i][0]=B2×f[i+1][0]+2B×(B1)×(totlen[i+1][0]+tot[i+1][0]+1)+B×nums[i]×f[i+1][1]+2nums[i]×(nums[i]1)×(totlen[i+1][1]+tot[i+1][1])

f [ i ] [ 1 ] = B × f [ i + 1 ] [ 1 ] + n u m s [ i ] × ( t o t l e n [ i + 1 ] [ 1 ] + t o t [ i + 1 ] [ 1 ] ) f[i][1]=B\times f[i+1][1]+nums[i]\times (totlen[i+1][1]+tot[i+1][1]) f[i][1]=B×f[i+1][1]+nums[i]×(totlen[i+1][1]+tot[i+1][1])

我们接着考虑如何转移 t o t tot tot t o t l e n totlen totlen

拼接之后的 t o t tot tot是在原有的后缀数量得到的,每个后缀与一个 x x x新生成一个,同时,当我们不卡上界时,我们可以在当前位置放置第一个非0数码,前面所有放置位置为前导0,即

t o t [ i ] [ 0 ] = ∑ x = 0 B − 1 t o t [ i + 1 ] [ 0 ] + ∑ x = 0 n u m s [ i ] − 1 t o t [ i + 1 ] [ 1 ] + B − 1 tot[i][0]=\sum_{x=0}^{B-1}tot[i+1][0]+\sum_{x=0}^{nums[i]-1}tot[i+1][1]+B-1 tot[i][0]=x=0B1tot[i+1][0]+x=0nums[i]1tot[i+1][1]+B1

t o t [ i ] [ 1 ] = ∑ x = n u m s [ i ] n u m s [ i ] t o t [ i + 1 ] [ 1 ] = t o t [ i + 1 ] [ 1 ] tot[i][1]=\sum_{x=nums[i]}^{nums[i]}tot[i+1][1]=tot[i+1][1] tot[i][1]=x=nums[i]nums[i]tot[i+1][1]=tot[i+1][1]

其中,在当前位置放置第一个非0数码的情形本来是 B B B种,但是由于此时计算不卡上界,所以需要-1

t o t l e n totlen totlen同样是在原来的后缀总长度上得到的,每个后缀与一个 x x x拼接,长度+1。每个 x x x能与 t o t [ i + 1 ] [ 0 / 1 ] tot[i+1][0/1] tot[i+1][0/1]个后缀拼接,这 t o t [ i + 1 ] [ 0 / 1 ] tot[i+1][0/1] tot[i+1][0/1]个后缀的总长度为 t o t l e n [ i + 1 ] [ 0 / 1 ] totlen[i+1][0/1] totlen[i+1][0/1],于是

t o t l e n [ i ] [ 0 ] = ∑ x = 0 B − 1 ( t o t l e n [ i + 1 ] [ 0 ] + t o t [ i + 1 ] [ 0 ] ) + ∑ x = 0 n u m s [ i ] − 1 ( t o t l e n [ i + 1 ] [ 1 ] + t o t [ i + 1 ] [ 0 ] + B − 1 ) totlen[i][0]=\sum_{x=0}^{B-1}(totlen[i+1][0]+tot[i+1][0])+\sum_{x=0}^{nums[i]-1}(totlen[i+1][1]+tot[i+1][0]+B-1) totlen[i][0]=x=0B1(totlen[i+1][0]+tot[i+1][0])+x=0nums[i]1(totlen[i+1][1]+tot[i+1][0]+B1)

t o t l e n [ i ] [ 1 ] = ∑ x = n u m s [ i ] n u m s [ i ] ( t o t l e n [ i + 1 ] + t o t [ i + 1 ] [ 1 ] ) totlen[i][1]=\sum_{x=nums[i]}^{nums[i]}(totlen[i+1]+tot[i+1][1]) totlen[i][1]=x=nums[i]nums[i](totlen[i+1]+tot[i+1][1])

其中 B − 1 B-1 B1的解释是与 t o t tot tot转移的是一样的,这里就不计算化简了。

最后如何统计答案

g s x g_{sx} gsx为拼接数 s x sx sx的答案,显然他的答案包括两部分,前面的答案 g s g_{s} gs与拼接 x x x的贡献,即

g s x = g s + f s x g_{sx}=g_{s}+f_{sx} gsx=gs+fsx

每个 x x x都可以贡献上 g s g_{s} gs,于是

g [ i ] [ 0 ] = ∑ x = 0 B − 1 ( g s + d e v x ) + ∑ x = 0 n u m s [ i ] ( g s + d e v x ) g[i][0]=\sum_{x=0}^{B-1}(g_{s}+dev_{x})+\sum_{x=0}^{nums[i]}(g_{s}+dev_{x}) g[i][0]=x=0B1(gs+devx)+x=0nums[i](gs+devx)

其中 d e v x dev_{x} devx即添加一个特定 x x x的贡献,我们已经计算有

f [ i ] [ 0 ] = ∑ x = 0 B − 1 d e v x + ∑ x = 0 n u m s [ i ] − 1 d e v x f[i][0]=\sum_{x=0}^{B-1}dev_{x}+\sum_{x=0}^{nums[i]-1}dev_{x} f[i][0]=x=0B1devx+x=0nums[i]1devx

于是

g [ i ] [ 0 ] = B × g [ i + 1 ] [ 0 ] + n u m s [ i ] × g [ i + 1 ] [ 1 ] + f [ i ] [ 0 ] g[i][0]=B\times g[i+1][0]+nums[i]\times g[i+1][1]+f[i][0] g[i][0]=B×g[i+1][0]+nums[i]×g[i+1][1]+f[i][0]

同理可得

g [ i ] [ 1 ] = ∑ x = n u m s [ i ] n u m s [ i ] ( g [ i + 1 ] [ 1 ] + d e v x ) g[i][1]=\sum_{x=nums[i]}^{nums[i]}(g[i+1][1]+dev_{x}) g[i][1]=x=nums[i]nums[i](g[i+1][1]+devx)

化简即

g [ i ] [ 1 ] = g [ i + 1 ] [ 1 ] × 1 + f [ i ] [ 1 ] g[i][1]=g[i+1][1]\times 1+f[i][1] g[i][1]=g[i+1][1]×1+f[i][1]

于是分别计算 [ 0 , r ] [0,r] [0,r] [ 0 , l − 1 ] [0,l-1] [0,l1]即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

using ll=long long;
const int N=100005;
const long long mod=20130427;

ll tot[N][2],totlen[N][2],g[N][2],f[N][2],B;

inline ll calc(int k){
	return 1ll*k*(k-1)/2%mod;
}

inline void mmod(ll &k){
	k=(k%mod+mod)%mod;
}

ll solve(ll *nums,int n)
{
	memset(tot,0,sizeof(tot));
	memset(totlen,0,sizeof(totlen));
	memset(g,0,sizeof(g));
	memset(f,0,sizeof(f));
	tot[n][0]=nums[n]-1; tot[n][1]=1;
	totlen[n][0]=nums[n]-1; totlen[n][1]=1;
	f[n][0]=calc(nums[n]); f[n][1]=nums[n];
	g[n][0]=f[n][0]; g[n][1]=f[n][1];
	for(int i=n-1;i>=1;i--)
	{
		tot[i][0]=B*tot[i+1][0]%mod+nums[i]*tot[i+1][1]%mod+B-1;
		tot[i][1]=tot[i+1][1];
		totlen[i][0]=B*(totlen[i+1][0]+tot[i+1][0])%mod+nums[i]*(totlen[i+1][1]+tot[i+1][1])%mod+B-1;
		totlen[i][1]=totlen[i+1][1]+tot[i+1][1];
		f[i][0]=B*B*f[i+1][0]%mod+calc(B)*(totlen[i+1][0]+tot[i+1][0]+1)%mod+B*nums[i]*f[i+1][1]%mod+calc(nums[i])*(totlen[i+1][1]+tot[i+1][1])%mod;
		f[i][1]=f[i+1][1]*B%mod+nums[i]*(totlen[i+1][1]+tot[i+1][1])%mod;
		g[i][0]=g[i+1][0]*B%mod+g[i+1][1]*nums[i]%mod+f[i][0];
		g[i][1]=g[i+1][1]*1%mod+f[i][1];
		for(int j=0;j<=1;j++) mmod(tot[i][j]),mmod(totlen[i][j]),mmod(f[i][j]),mmod(g[i][j]);
	}
	return (g[1][0]+g[1][1])%mod;
}

ll nums[3][N];
int len[3];

int main()
{
	cin>>B;
	for(int i=1;i<=2;i++)
	{
		cin>>len[i];
		for(int j=len[i];j>=1;j--) cin>>nums[i][j];
	}
	nums[1][1]--;
	for(int i=1;i<=len[1];i++)
		if(nums[1][i]<0) nums[1][i]+=B,nums[1][i+1]--;
	if(nums[1][len[1]]==0) len[1]--;
	printf("%lld\n",((solve(nums[2],len[2])-solve(nums[1],len[1]))%mod+mod)%mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值