[ZJOI2010]数字计数

题意

给定两个正整数 \(a\)\(b\),求在 \([a,b]\)中的所有整数中,每个数码(digit)各出现了多少次。

输入 
1 99
输出 
9 20 20 20 20 20 20 20 20 20

算法

最近好像喜欢上数位DP了
这是一个三维的DP
\(dp[i][j][k]\) 设从后往前填到第\(i\)位 这一位上的数字是\(j\) \(k\)这个数字出现的次数

初始化代码

void init(){
	for(ll i = 0;i <= 9;i++)
	dp[1][i][i] = 1;	
	for(ll i = 2;i <= 12;i++)//第几位
	for(ll now = 0;now <= 9;now++){//当前的数字
	dp[i][now][now] = pow(10,i - 1);//先计算现在填的这位的贡献
	for(ll k = 0;k <= 9;k++)//上一位的数字		
	for(ll j = 0;j <= 9;j++)//数字
	dp[i][now][j] += dp[i - 1][k][j];
	}
}

我比较习惯在数位DP的时候把DP值求出来 然后根据要求的数字来求解
对于指定的数字求出答案的细节看下面的代码

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long

using namespace std;

ll dp[13][10][10],ans[10],tans[10],a[13],an,bn;

void init(){
	for(ll i = 0;i <= 9;i++)
	dp[1][i][i] = 1;	
	for(ll i = 2;i <= 12;i++)
	for(ll now = 0;now <= 9;now++){
	dp[i][now][now] = pow(10,i - 1);
	for(ll k = 0;k <= 9;k++)		
	for(ll j = 0;j <= 9;j++)
	dp[i][now][j] += dp[i - 1][k][j];
	}
}

void solve(ll x){
	ll cnt = 0;
	while(x){
		a[++ cnt] = x % 10;
		x = x / 10;
	}//惯用分解数字
	memset(ans,0,sizeof(ans)); 
	for(ll i = 1;i < cnt;i++){//先统计位数比x少的数
		for(ll j = 1;j <= 9;j++)
			for(ll k = 0;k <= 9;k++)
			ans[k] += dp[i][j][k];
	}
	for(ll i = 1;i < a[cnt];i++)//位数同x一样但是最高位比x最高位小的
	for(ll k = 0;k <= 9;k++)
	ans[k] += dp[cnt][i][k];
	for(ll i = cnt - 1;i >= 1;i--){//位数同x一样最高位也和x一样的
	for(ll j = 0;j < a[i];j++)
	for(ll k = 0;k <= 9;k++)
	ans[k] += dp[i][j][k];
	for(ll p = cnt;p > i;p--)
	ans[a[p]] += a[i] * pow(10,i - 1);
	}
	return ;
}

int main(){
	scanf("%lld%lld",&an,&bn);
	init();
	solve(bn + 1);//solve这个函数求的是[0,x)的答案
	for(ll i = 0;i <= 9;i++)
        tans[i] += ans[i];
        solve(an);
        for(ll i = 0;i <= 9;i++)
        tans[i] -= ans[i];
        for(ll i = 0;i <= 9;i++)
        cout<<tans[i]<<" ";
        return 0;
}

posted @ 2020-10-13 20:30  fhq_treap  阅读(81)  评论(0编辑  收藏  举报