测试地址:数数
题目大意: 给定
两个
位内的
进制数,
,对区间
内的所有数
,累加
中所有子串表示的数字的和(如
,应该累加
到答案中,注意不应该包含前导零),求最终答案(用
进制表示)。
做法: 本题需要用到数位DP。
神题。虽然一眼能看出数位DP,但具体的转移式子还是想错了好多回,这次终于写对了。
首先我们来看,对于一个
位的数
,在它末尾加一位数
,会对这个数的所有子串表示的数字和有什么影响(以下简写成
)。令
表示数
所有后缀表示的数字和,那么有:
根据这个为基础,我们就能思考数位DP的转移了。
根据套路,首先把问题转化为:用小于等于
的所有数的贡献,减去小于等于
的所有数的贡献,于是现在我们考虑求小于等于某个数
时的贡献。
从高位向低位枚举,令
表示前
位中,不卡/卡上界的所有数的
的和,
表示前
位中,不卡/卡上界的所有数的
的和。先分析具体的转移过程:
前
位的数不卡上界时,第
位可以填
~
内所有的数,并且新的数都不卡上界;
前
位的数卡上界时,第
位可以填
~
。当填
~
时,新的数不卡上界,当填
时新的数卡上界。
于是我们先考虑
的转移。首先,卡上界的情况应该很好转移了,实际上就是求上界的
值。主要是不卡上界的情况比较复杂。
首先考虑不卡上界转移到不卡上界的情况。根据上面的转移式子
,我们这样考虑:首先枚举
从
到
,对于每一个
,再枚举可转移的
,把贡献累加起来。于是一个
对整个
的贡献是:
,那么对于所有
,对
的贡献就是:
。其中
就是
,而
需要斟酌一下。这个和式是在求,对于所有可转移的
(包括
),累加它们的位数
(把
的位数看做
)。观察规律,我们发现:
有
个,
有
个,
有
个,
有
个…
有
个,
有
个,
表示
的前
位组成的前缀。那么前面的有规律的部分可以递推维护,而
显然也可以递推维护,所以我们就可以每次
地进行这个转移了。
接下来考虑卡上界转移到不卡上界的情况。这种情况下,能转移到不卡上界的情况,
必须是
~
,而且因为这种情况中可转移的
只有一个,而且
就是
,因此就比上面的情况简单很多了,总贡献应该为
。
那么
的转移讨论完了,接下来讨论
的转移。
就是求上界的
,而
也利用上面的思考方式,先考虑从不卡上界转移的情况,因为有
种转移,所以
就产生了
次的贡献。再考虑从卡上界转移的情况,因为有
种转移,所以
就产生了
次的贡献。再加上所有不卡上界数的后缀产生的贡献,即
,就可以计算出
了。
至此,经过漫长的讨论,我们得到了一个
的数位DP,完美地解决了这一道题。
以下是本人代码:
#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;
}