P2602 [ZJOI2010]数字计数(数位dp)

题目描述

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

输入输出格式

输入格式:

输入文件中仅包含一行两个整数a、b,含义如上所述。

输出格式:

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

输入输出样例

输入样例#1: 复制

1 99

输出样例#1: 复制

9 20 20 20 20 20 20 20 20 20

说明

30%的数据中,a<=b<=10^6;

100%的数据中,a<=b<=10^12。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <set>
#include <cstring>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#define Swap(a,b)  a ^= b ^= a ^= b
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std ;
typedef long long LL;
const int N = 1e7+10 ;

LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL lcm(LL a,LL b){return a/gcd(a,b)*b;}
const int MAX = 105;
const int inf = 0xffffff;
const LL mod = 1e9+7 ;
priority_queue<int,vector<int>,greater<int> > q ;// 小跟堆
priority_queue<int> Q ; // 大堆
LL a[20] ;
LL dp[20][20] ;
LL dfs(int pos , bool limit , bool lead , int dig ,LL sum){
	// limit为1 表示有限制 , lead 为0 表示有 前道0
	LL ans = 0 ;
	if(pos == 0 ){
		return sum ;
	}
	if(!limit&& lead&&dp[pos][sum]!=-1){// 合法
		//记忆化
		return dp[pos][sum] ;
	}
	int up = limit ?a[pos]:9;// 如果该位有限制,就设为a[pos],否则,0~9都可以呀
	for(int j = 0 ; j<=up ;j++){
		ans+=dfs(pos-1,(j==up)&&limit ,lead||j ,dig,sum+((j||lead)&&(j==dig)));
	}
	if(!limit&&lead){
		dp[pos][sum] = ans ;
	}
	return ans ;

}
LL solve(LL x ,int d){
	int pos = 0 ;
	memset(dp,-1,sizeof(dp)); // 初始化
	while(x){
		a[++pos]=x%10;
		x/=10 ;
	}
	return dfs(pos,1,0,d,0);
}
int main()
{
	ios_base::sync_with_stdio(false);
    cin.tie(NULL),cout.tie(NULL);
	LL l , r ;
	cin >> l >>r ;
	for(int i = 0 ; i<=9 ; i++ ){
	
		cout<<solve(r,i)-solve(l-1,i)<<" ";
	
	}
	
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41661809/article/details/86680433