洛谷p2602:数字计数

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

一开始想歪了,想去枚举每一位,然后统计每一位的情况。
转念一想,数位dp是统计[a,b]内符合条件的数字的个数。

换一种方法,对key~[0,9],统计含1个key,2个key…len(len为数字的长度)个key的数字,然后加一下,就可以得到答案了

#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
long long a,b;
int g[30];
long long dp[30][30][2];
long long dfs(int key,int num,int target,int pos,bool limit,bool sta){
 	if(num>target) return 0;   
 	if(pos==0){
  		if(key!=0){
   			if(num==target) return 1;
   			return 0;
  		}
  		else{
   			if(num==target&&sta) return 1;
   			return 0;
  		}
 	}
 	if(!limit&&dp[pos][num][sta]!=-1) return dp[pos][num][sta];
 	int up=limit?g[pos]:9;
 	long long res=0;
 	for(int i=0;i<=up;i++){
 	 	if(key==0){
   			if(i==0) 
    				if(sta) res+=dfs(key,num+1,target,pos-1,limit&&i==g[pos],true);
    				else res+=dfs(key,0,target,pos-1,limit&&i==g[pos],false);
   			else
    				res+=dfs(key,num,target,pos-1,limit&&i==g[pos],sta||i>0);
  		}
  		else{
   			if(i==key)
    				res+=dfs(key,num+1,target,pos-1,limit&&i==g[pos],sta);
   			else res+=dfs(key,num,target,pos-1,limit&&i==g[pos],sta);
  		}
 	}
 	if(!limit) dp[pos][num][sta]=res;
 	return res;
}
long long solve(long long x,int key){
 	long long ans=0;
 	int pos=0;
 	while(x){
  		g[++pos]=x%10;
  		x/=10;
 	}
 	for(int i=1;i<=pos;i++){
  		memset(dp,-1,sizeof(dp));
  		ans=ans+i*dfs(key,0,i,pos,true,false);
 	}
 	return ans;
}
int main(){
 	scanf("%lld%lld",&a,&b);
 	for(int i=0;i<=9;i++){
  		if(i) printf(" ");
  		printf("%lld",solve(b,i)-solve(a-1,i));
 	}
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/88063253