P2602 [ZJOI2010] 数字计数 题解

数位dp的板子题?

显然 \([a,b]\) 等价于 \([0,b]-[0,a]\)

考虑 \(dp_{i,j}\) 表示到第 \(i\) 位数字 \(j\) 的答案。先不考虑数字大小限制(即1到999之类),则显然有 \(\large dp_{i,j}=dp_{i-1,j} \times10+10^{i-1}\),当前数字是 \(0\) 时则减去 \(10^{i-1}\),再减去 \(1\)

所以我们可以预处理出\(dp\),来表示后面这一坨。

实际操作的时候,我们可以不用写两维,写一维的cnt数组记录一下就可以。

再加上限制,当前数的方案数我们由小于等于它的最大999……9和剩余部分拼成。后面这一部分就是各位数字上的数乘\(dp_i\)之和。

加上比当前位小的数多出现了\(10^{i-1}\)次,当前位显然多出现了后面位组成的数字次,然后前导零其实就是\(\sum_{i=1}^{len}10^i\),减去就行。

#include<bits/stdc++.h>
using namespace std;
long long a,b,l,r,num;
int numa[15],numb[15];
long long cnt[11];
long long f[15];
long long po[15]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000,100000000000,100000000000,};
int main(){
	for(int i=1;i<=15;++i) f[i]=i*po[i-1];
	scanf("%lld %lld",&a,&b);
	if(a>b) swap(a,b);
	--a;
	while(a) numa[++l]=a%10,a/=10;
	while(b) numb[++r]=b%10,b/=10;
	for(int i=l;i>=1;--i){
		for(int j=0;j<=9;++j) cnt[j]-=numa[i]*f[i-1];
		for(int j=0;j<numa[i];++j) cnt[j]-=po[i-1];
		num=0;
		for(int j=i-1;j>=1;--j) num=num*10+numa[j];
		cnt[numa[i]]-=(num+1);
		cnt[0]+=po[i-1];
	}
	for(int i=r;i>=1;--i){
		for(int j=0;j<=9;++j) cnt[j]+=numb[i]*f[i-1];
		for(int j=0;j<numb[i];++j) cnt[j]+=po[i-1];
		num=0;
		for(int j=i-1;j>=1;--j) num=num*10+numb[j];
		cnt[numb[i]]+=(num+1);
		cnt[0]-=po[i-1];
	}
	for(int i=0;i<=9;++i) printf("%lld ",cnt[i]);
	return 0;
}
posted @ 2024-09-15 21:04  mountzhu  阅读(34)  评论(0)    收藏  举报