数位dp入门题.
首先考虑这样的一个问题:定义
d
p
i
dp_i
dpi为满
i
i
i位数字中,每个数字出现次数.
因为满
i
i
i位的关系,每个数字的出现次数一定是一样的.
考虑这个数字是
j
j
j,首先,第
i
i
i位随便放一个数字(不管放不放
j
j
j,这个
j
j
j都暂时不要算进方案里面),然后用前
i
−
1
i-1
i−1位的方案.
有
d
p
i
=
10
∗
d
p
i
−
1
dp_i=10*dp_{i-1}
dpi=10∗dpi−1
然后,我们确定第
i
i
i位直接放
j
j
j这个数字,此时
j
j
j的贡献算1,余下的
i
−
1
i-1
i−1位不算进答案
有
d
p
i
=
1
0
i
−
1
dp_i=10^{i-1}
dpi=10i−1
合起来就是
d
p
i
=
d
p
i
∗
10
+
1
0
i
−
1
dp_i=dp_i*10+10^{i-1}
dpi=dpi∗10+10i−1
这样我们可以解决以下的一个问题:第
i
i
i位是A,剩下的
i
−
1
i-1
i−1位是没有确定的情况下
每个数字出现的次数
首先是考虑第
i
i
i位取
0
到
A
−
1
0到A-1
0到A−1中的任意一个数字,那么因为没有取到
A
A
A,后边数字可以任意地取,此时贡献是
A
∗
d
p
[
i
−
1
]
A*dp[i-1]
A∗dp[i−1],注意,此时第
i
i
i位没有纳入贡献.
考虑第
i
i
i位的取值,如果取
0
到
A
−
1
0到A-1
0到A−1,与
d
p
数组类似
dp数组类似
dp数组类似,这些数字的出现次数需要加上
1
0
i
−
1
10^{i-1}
10i−1
特别地,考虑第
i
i
i位取A,我们需要十分地谨慎.此时我们要求
i
−
1
i-1
i−1位上只能取
0
到
n
u
m
[
i
]
0到num[i]
0到num[i]
接下来就是求这个
i
−
1
i-1
i−1位,每个位上取
0
到
n
u
m
[
i
]
0到num[i]
0到num[i]的数字个数.让这个东西为
c
n
t
cnt
cnt
这个东西说简单简单,说恶心也恶心,主要思路就是检查每个位置上到底有多少个数字经过.打个比方如数字
792
792
792,个位上一定经过了
792
792
792个数字,十位上经过了
79
79
79个数字,百位上经过了
7
7
7个数字.
最终答案是
792
+
79
+
7
792+79+7
792+79+7.我们发现什么?不就每个位置从高位开始前缀相加吗
计算完这个前缀值,不要忘了A本身也要加1.
最后一个问题:扣去包含前导0的方案,001这种数字前边的0不应当算进0的数目,应当扣掉.我们讨论有
i
i
i个前导0,后边的数字任取.
这些前导0分位来讨论,分别有
∑
i
=
1
n
−
1
1
0
i
\sum_{i=1}^{n-1}10^i
∑i=1n−110i,到
i
−
1
i-1
i−1是因为每个位置的前导0
第一次学这个数位dp,还不是很熟练,需要多加练习
/*
I don't wanna be left behind,Distance was a friend of mine.
Catching breath in a web of lies,I've spent most of my life.
Catching my breath letting it go turning my cheek for the sake of this show
Now that you know this is my life I won't be told what's supposed to be right
Catch my breath no one can hold me back I ain't got time for that.
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
#define pb(a) push_back(a)
ll cnta[15],cntb[15];
ll pw[15];//pw[i] = 10^i
ll s[maxn];ll dp[15];
void get(ll n,ll *ans){
int len = 0;memset(s,0,sizeof(s));
while(n) s[++len] = n%10,n/=10;
for(int i=len;i>=1;i--){
for(int j=0;j<=9;j++) ans[j]+=dp[i-1] * s[i];
for(int j=0;j<s[i];j++) ans[j]+=pw[i-1];//0~i-1任意取
ll cnt = 0;//计算若干前缀的和(1,i-1)
for(int j=i-1;j>=1;j--) cnt = cnt*10+s[j];
ans[0]-=pw[i-1];
ans[s[i]] +=(cnt+1);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll L,R;cin>>L>>R;
pw[0] = 1;
for(int i=1;i<=15;i++){
dp[i] = dp[i-1]*10 + pw[i-1];
pw[i] = pw[i-1]*10;
}
get(R,cnta);
get(L-1,cntb);
for(int i=0;i<=9;i++) cout<<(cnta[i]-cntb[i])<<" ";
}