测试地址:数数
题目大意: 给定
L
,
R
L,R
L,R两个
1
0
5
10^5
105位内的
B
(
≤
1
0
5
)
B(\le 10^5)
B(≤105)进制数,
L
≤
R
L\le R
L≤R,对区间
[
L
,
R
]
[L,R]
[L,R]内的所有数
x
x
x,累加
x
x
x中所有子串表示的数字的和(如
123
123
123,应该累加
123
+
12
+
23
+
1
+
2
+
3
123+12+23+1+2+3
123+12+23+1+2+3到答案中,注意不应该包含前导零),求最终答案(用
10
10
10进制表示)。
做法: 本题需要用到数位DP。
神题。虽然一眼能看出数位DP,但具体的转移式子还是想错了好多回,这次终于写对了。
首先我们来看,对于一个
l
e
n
len
len位的数
x
x
x,在它末尾加一位数
s
s
s,会对这个数的所有子串表示的数字和有什么影响(以下简写成
s
u
m
(
x
)
sum(x)
sum(x))。令
s
u
f
(
x
)
suf(x)
suf(x)表示数
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)
suf(xnew)=suf(xlast)⋅B+s⋅(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})
sum(xnew)=sum(xlast)+suf(xnew)
根据这个为基础,我们就能思考数位DP的转移了。
根据套路,首先把问题转化为:用小于等于
R
R
R的所有数的贡献,减去小于等于
L
−
1
L-1
L−1的所有数的贡献,于是现在我们考虑求小于等于某个数
T
T
T时的贡献。
从高位向低位枚举,令
S
u
m
(
i
,
0
/
1
)
Sum(i,0/1)
Sum(i,0/1)表示前
i
i
i位中,不卡/卡上界的所有数的
s
u
m
(
x
)
sum(x)
sum(x)的和,
S
u
f
(
i
,
0
/
1
)
Suf(i,0/1)
Suf(i,0/1)表示前
i
i
i位中,不卡/卡上界的所有数的
s
u
f
(
x
)
suf(x)
suf(x)的和。先分析具体的转移过程:
前
i
−
1
i-1
i−1位的数不卡上界时,第
i
i
i位可以填
0
0
0 ~
B
−
1
B-1
B−1内所有的数,并且新的数都不卡上界;
前
i
−
1
i-1
i−1位的数卡上界时,第
i
i
i位可以填
0
0
0 ~
T
[
i
]
T[i]
T[i]。当填
0
0
0 ~
T
[
i
]
−
1
T[i]-1
T[i]−1时,新的数不卡上界,当填
T
[
i
]
T[i]
T[i]时新的数卡上界。
于是我们先考虑
S
u
f
Suf
Suf的转移。首先,卡上界的情况应该很好转移了,实际上就是求上界的
s
u
f
suf
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)
suf(xnew)=suf(xlast)⋅B+s⋅(len+1),我们这样考虑:首先枚举
s
s
s从
0
0
0到
B
−
1
B-1
B−1,对于每一个
s
s
s,再枚举可转移的
x
l
a
s
t
x_{last}
xlast,把贡献累加起来。于是一个
s
s
s对整个
S
u
f
Suf
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)
B⋅∑suf(xlast)+s⋅∑(len(xlast)+1),那么对于所有
s
s
s,对
S
u
f
Suf
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)
B⋅B⋅∑suf(xlast)+2B(B−1)⋅∑(len(xlast)+1)。其中
∑
s
u
f
(
x
l
a
s
t
)
\sum suf(x_{last})
∑suf(xlast)就是
S
u
f
(
i
−
1
,
0
)
Suf(i-1,0)
Suf(i−1,0),而
∑
(
l
e
n
(
x
l
a
s
t
)
+
1
)
\sum (len(x_{last})+1)
∑(len(xlast)+1)需要斟酌一下。这个和式是在求,对于所有可转移的
x
l
a
s
t
x_{last}
xlast(包括
0
0
0),累加它们的位数
+
1
+1
+1(把
0
0
0的位数看做
0
0
0)。观察规律,我们发现:
1
1
1有
1
1
1个,
2
2
2有
B
−
1
B-1
B−1个,
3
3
3有
(
B
−
1
)
⋅
B
(B-1)\cdot B
(B−1)⋅B个,
4
4
4有
(
B
−
1
)
⋅
B
2
(B-1)\cdot B^2
(B−1)⋅B2个…
i
−
1
i-1
i−1有
(
B
−
1
)
⋅
B
i
−
3
(B-1)\cdot B^{i-3}
(B−1)⋅Bi−3个,
i
i
i有
n
u
m
−
B
i
−
2
num-B^{i-2}
num−Bi−2个,
n
u
m
num
num表示
T
T
T的前
i
−
1
i-1
i−1位组成的前缀。那么前面的有规律的部分可以递推维护,而
n
u
m
num
num显然也可以递推维护,所以我们就可以每次
O
(
1
)
O(1)
O(1)地进行这个转移了。
接下来考虑卡上界转移到不卡上界的情况。这种情况下,能转移到不卡上界的情况,
s
s
s必须是
0
0
0 ~
T
[
i
]
−
1
T[i]-1
T[i]−1,而且因为这种情况中可转移的
x
l
a
s
t
x_{last}
xlast只有一个,而且
l
e
n
(
x
l
a
s
t
)
len(x_{last})
len(xlast)就是
i
−
1
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
Suf(i−1,1)⋅B+2T[i](T[i]−1)⋅i。
那么
S
u
f
Suf
Suf的转移讨论完了,接下来讨论
S
u
m
Sum
Sum的转移。
S
u
m
(
i
,
1
)
Sum(i,1)
Sum(i,1)就是求上界的
s
u
m
sum
sum,而
S
u
m
(
i
,
0
)
Sum(i,0)
Sum(i,0)也利用上面的思考方式,先考虑从不卡上界转移的情况,因为有
B
B
B种转移,所以
S
u
m
(
i
−
1
,
0
)
Sum(i-1,0)
Sum(i−1,0)就产生了
B
B
B次的贡献。再考虑从卡上界转移的情况,因为有
T
[
i
]
T[i]
T[i]种转移,所以
S
u
m
(
i
−
1
,
1
)
Sum(i-1,1)
Sum(i−1,1)就产生了
T
[
i
]
T[i]
T[i]次的贡献。再加上所有不卡上界数的后缀产生的贡献,即
S
u
f
(
i
,
0
)
Suf(i,0)
Suf(i,0),就可以计算出
S
u
m
(
i
,
0
)
Sum(i,0)
Sum(i,0)了。
至此,经过漫长的讨论,我们得到了一个
O
(
n
)
O(n)
O(n)的数位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;
}