UVA1640 统计问题 The Counting Problem

Link\text{Link}

题意

统计正整数 aba\sim b090\sim9 每个数码出现的次数。

分析

其实就是计算两次 1x1\sim x 中每个数码 numnum 出现的次数,前缀和相减就是答案。

我们设 ask(x,num)\text{ask}(x,num) 表示 1x1\sim x 中数码 numnum 出现的次数,先把数 xx 按位拆分,设共有 nn 位,个位到第 nn 位分别存在 a1na_{1\sim n} 中,为了方便后面的计算,再求出 bib_i 表示 xx 的第 ii 位到个位组成的数。

然后开始求答案 ansans,众所周知,00 比较特殊,于是我们把 00 单独提出来处理,先解决当 num=19num=1\sim9 时怎么做。

dfs(p,num)\text{dfs}(p,num) 表示 xx 的第 pp 位之前全部填 ap+1na_{p+1\sim n} 时第 1p1\sim p 位的可能的数码 numnum 的个数,特别的,当 p=np=n 时,也认为有一个虚拟的第 n+1n+1 位填了最大的限制。

因此当 num=19num=1\sim 9 时,ask(x,num)\text{ask}(x,num)dfs(n,num)\text{dfs}(n,num)

然后写 dfs(p,num)\text{dfs}(p,num)。我们把统计第 1p1\sim p 位分成第 pp 位和第 1p11\sim p-1 位来算:

  • 求第 pp 位填 numnum 时的数的个数:若 num<apnum<a_p,这一位填 numnum 时前面的位已经固定,后面的则可以为 010p110\sim 10^{p-1}-110p110^{p-1} 种数;若 num=apnum=a_p,则 pnp\sim n 位都填了最大值,剩下的位可以填 bp1+1b_{p-1}+1 种数;若 num>apnum>a_p,则不可能填。

  • 求第 1p11\sim p-1 位填 numnum 时的数的个数:若第 pp 位填 apa_p,则后面的位子填的 numnum 的个数就是 dfs(p1,num)\text{dfs}(p-1,num);若第 pp 位填 0ap10\sim a_p-1,则此时后面的填法时没有限制的,设 dpi,jdp_{i,j} 表示 010i10\sim 10^i-1 中的所有数的数码 jj 的个数的总和,则答案为 dpp1,num×apdp_{p-1,num}\times a_p

对于 num=0num=0 的情况,最高位不能填 numnum,只要求出第 nn 位填 1an1\sim a_n 时的答案和所有 1n11\sim n-1 位数中 numnum 的个数再相加就是答案。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10;
ll dp[N][N],m10[N]={1},a[N],b[N];
ll dfs(int p,int num){
	if(p==0)
		return 0;
	ll ans=0;
	if(num<a[p])
		ans+=m10[p-1];
	else if(num==a[p])
		ans+=b[p-1]+1;
	ans+=dp[p-1][num]*a[p];
	ans+=dfs(p-1,num);
	return ans;
}
ll ask(ll x,int num){
	if(x<10)
		return num<=x;
	int n=0;
	while(x){
		a[++n]=x%10;
		x/=10;
		b[n]=a[n]*m10[n-1]+b[n-1];
	}
	ll ans=0;
	if(num==0){
		ans=dp[n-1][num]*(a[n]-1);
		ans+=dfs(n-1,num);
		ans+=ask(m10[n-1]-1,num);
	}
	else
		ans=dfs(n,num);
	return ans;
}
int main(){
	for(int i=0;i<=9;i++)
		dp[1][i]=1;
	for(int i=1;i<=9;i++)
		m10[i]=m10[i-1]*10;
	for(int i=2;i<=9;i++)
		for(int j=0;j<=9;j++)
			dp[i][j]=m10[i-1]+dp[i-1][j]*10;
	ll x,y;
	while(cin>>x>>y&&x&&y){
		if(x<y)
			swap(x,y);
		for(int i=0;i<9;i++)
			cout<<ask(x,i)-ask(y-1,i)<<" ";
		cout<<ask(x,9)-ask(y-1,9)<<endl;
	}
	return 0;
}

注意输出不能多行末空格。

posted @ 2022-04-22 13:38  luckydrawbox  阅读(13)  评论(0)    收藏  举报  来源