神奇的数位dp。主要思路就是一个试填法。不做参考,就是存一下代码。
传送门
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<algorithm>
#include<cstdlib>
#define LL long long
using namespace std;
LL mul[16];
struct CNT {
LL a[10];
};
CNT operator + (CNT a,CNT b) {
for(int i=0; i<10; i++) {
a.a[i]+=b.a[i];
}
return a;
}
LL l,r;
CNT f[25][10];//表示前i位的开头为第j位的第k个数出现了a[k]次。
CNT cal(LL x) {
CNT ans;
for(int i=0; i<=9; i++)ans.a[i]=0;
if(x==0) {
ans.a[0]=1;
return ans;
}
int len=15;
while(mul[len]>x)len--;
for(int i=1; i<len; i++) {
for(int j=1; j<=9; j++) {
ans=ans+f[i][j];/因为预处理好了前i位以j为开头的各个位数有多少个了,然后就填len-1位的。
}
}
ans.a[0]++;
int cur=x/mul[len];
for(int i=1;i<cur;i++){
ans=ans+f[len][i];//填len位开头的数,乱搞一波。。。应该能看懂吧。。
}
x%=mul[len];
ans.a[cur]+=x+1;
for(int i=len-1;i;i--){
cur=x/mul[i];
for(int j=0;j<cur;j++){
ans=ans+f[i][j];
}
x%=mul[i];
ans.a[cur]+=x+1;
}
return ans;
}
int main() {
cin>>l>>r;
mul[1]=1;
for(int i=2; i<=15; i++) {
mul[i]=mul[i-1]*10;
}
for(int i=0; i<=9; i++)f[1][i].a[i]=1;
for(int i=2; i<=12; i++) {//预处理。易知。
for(int j=0; j<=9; j++) {
for(int k=0; k<=9; k++) {
f[i][k]=f[i][k]+f[i-1][j];
f[i][k].a[k]+=mul[i-1];
}
}
}
CNT a=cal(l-1),b=cal(r);
for(int i=0;i<=9;i++){
printf("%lld ",b.a[i]-a.a[i]);
}
}