P2602 [ZJOI2010] 数字计数 题解

数位dp的板子题?

显然 [a,b] 等价于 [0,b][0,a]

考虑 dpi,j 表示到第 i 位数字 j 的答案。先不考虑数字大小限制(即1到999之类),则显然有 dpi,j=dpi1,j×10+10i1,当前数字是 0 时则减去 10i1,再减去 1

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

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

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

加上比当前位小的数多出现了10i1次,当前位显然多出现了后面位组成的数字次,然后前导零其实就是i=1len10i,减去就行。

#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 @   mountzhu  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示