洛谷P3281 数位dp

题意:

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

Solution:

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

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

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

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

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

其中lsl_{s}ls是其中一个sss数的长度,按照数位dpdpdp的思路,在iii位放置xxx,他能产生作用的是前面所能形成的所有数,于是

fsx=B×fs+x×(totlens+tots) f_{sx}=B\times f_{s}+x\times (totlen_{s}+tot_{s}) fsx=B×fs+x×(totlens+tots)

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

于是

f[i][0]=∑x=0B−1(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][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=nums[i]nums[i](B×f[i+1][1]+x×(totlen[i+1][1]+tot[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的情况,此时对于每个xxx都贡献一个xxx的量,之后化简可得

f[i][0]=B2×f[i+1][0]+B×(B−1)2×(totlen[i+1][0]+tot[i+1][0]+1)+B×nums[i]×f[i+1][1]+nums[i]×(nums[i]−1)2×(totlen[i+1][1]+tot[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]+nums[i]×(totlen[i+1][1]+tot[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])

我们接着考虑如何转移tottottottotlentotlentotlen

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

tot[i][0]=∑x=0B−1tot[i+1][0]+∑x=0nums[i]−1tot[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

tot[i][1]=∑x=nums[i]nums[i]tot[i+1][1]=tot[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数码的情形本来是BBB种,但是由于此时计算不卡上界,所以需要-1

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

totlen[i][0]=∑x=0B−1(totlen[i+1][0]+tot[i+1][0])+∑x=0nums[i]−1(totlen[i+1][1]+tot[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)

totlen[i][1]=∑x=nums[i]nums[i](totlen[i+1]+tot[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−1B-1B1的解释是与tottottot转移的是一样的,这里就不计算化简了。

最后如何统计答案

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

gsx=gs+fsx g_{sx}=g_{s}+f_{sx} gsx=gs+fsx

每个xxx都可以贡献上gsg_{s}gs,于是

g[i][0]=∑x=0B−1(gs+devx)+∑x=0nums[i](gs+devx) 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)

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

f[i][0]=∑x=0B−1devx+∑x=0nums[i]−1devx 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]+nums[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=nums[i]nums[i](g[i+1][1]+devx) 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值