题目
https://www.luogu.org/problemnew/show/P2602
思路
首先区间[a,b]内的数量可转化为[1,b]-[1,a-1]。考虑求一个数码出现的次数,比如1出现的次数。我们首先想爆搜怎么写,然后加一个记忆化即可。
对于一个长度为len的数,从高位到低位枚举它每一位上的数字,然后计算1出现的次数。哪些东西要记到状态里去?第一,当前的位置len一定要记的。第二,你要记录当前数位有没有比num[len]小,是一个bool值,因为这个用来确定你枚举下一位的范围。第三,要记录当前1已经出现的次数。第四,由于前导0不能算,还需记录之前是否是前导0,也是一个bool值。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=50;
ll a,b,s1[maxn],s[maxn],num[maxn],ten[maxn],f[maxn],cnt;
void init()
{
ten[0]=1;
for(int i=1; i<=15; i++) f[i]=f[i-1]*10+ten[i-1],ten[i]=ten[i-1]*10;
}
void work(ll x)
{
memset(s,0,sizeof(s)); cnt=0;
while(x)
{
num[++cnt]=x%10; x/=10;
}
for(int i=cnt; i>=1; i--)
{
for(int j=0; j<=9; j++) s[j]+=f[i-1]*num[i];
for(int j=0; j<num[i]; j++) s[j]+=ten[i-1];
ll num2=0;
for(int j=i-1; j>=1; j--) num2=num2*10+num[j];
s[num[i]]+=num2+1;
s[0]-=ten[i-1];
}
}
int main()
{
init();
scanf("%lld%lld",&a,&b);
work(b); for(int i=0; i<=9; i++) s1[i]=s[i];
work(a-1); for(int i=0; i<=9; i++) printf("%lld ",s1[i]-s[i]);
}