LG-P2602 [ZJOI2010]数字计数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xu0_zy/article/details/83449679

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

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

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

输入样例:
1 99
输出样例:
9 20 20 20 20 20 20 20 20 20
说明
30%的数据中,a<=b<=10^6;

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

题解
算是数位DP吧。(虽然感觉没用到DP)
这个直接针对每一位来处理就好了。

对于第 i 位为数字 j 的情况,我们可以直接根据 x 推出,而相反,在windy数中的技巧在这里难以施展。

对于一个数字我们把它拆成三部分来考虑。
1.第 i 位前面的部分,记作pre
2.第 i 位,记作pi
3.第 i 位后面的部分,记作sub
例如 1390,i=2时 的三部分分别为13,9,0;i=4时,三部分分别为0,3,90

第 i 位为某个数字 j 的情况
j &lt; p i t i m e s = ( p r e + 1 [ j = 0 ] ) 1 0 i 1 j&lt;pi:times=(pre+1-[j=0])*10^{i-1} 这一位小于 x 后面的低位随便,之前的范围是 0~pre,共 pre+1 个,但是当 j=0 时,pre 不能取到 0,否则就和之前的状态重复了。
j = p i t i m e s = ( p r e [ j = 0 ] ) 1 0 i 1 + s u b + 1 j=pi:times=(pre-[j=0])*10^{i-1}+sub+1 pre 同理,sub 的取值为 0 到 sub,共 sub+1 种情况。
j &gt; p i t i m e s = ( p r e [ j = 0 ] ) 1 0 i 1 j&gt;pi:times=(pre-[j=0])*10^{i-1}

综上即可求解。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=15;
#define LL long long
LL ten[maxn],q[maxn],h[maxn],A,B,ans[maxn];
int p[maxn],n;
void DPS(LL x,LL d)
{
	n=0;
	for (LL y=x;y;y/=10) p[++n]=y%10;
	if (!n) {ans[0]+=d;return;}
	q[n+1]=0;for (int i=n;i>=1;--i) q[i]=q[i+1]*10+p[i];q[n+1]=1;
	h[0]=0;  for (int i=1;i<=n;++i) h[i]=h[i-1]+p[i]*ten[i-1];
	for (int i=1;i<n;++i)
	  for (int j=0;j<10;++j)
	  {
		  LL pre=q[i+1]-(j>=p[i]?1:0),sub=h[i-1];
		  ans[j]+=(pre+(j>0))*ten[i-1]*d;
		  if (j==p[i]) ans[j]+=(sub+1)*d;
	  }
	
	for (int j=1;j<=p[n];++j)
	  if (j==p[n]) ans[j]+=(h[n-1]+1)*d;else ans[j]+=ten[n-1]*d;
}
int main()
{
	scanf("%lld%lld",&A,&B);*ten=1;
	for (int i=1;i<=12;++i) ten[i]=ten[i-1]*10;
	DPS(B,1);DPS(A-1,-1);
	for (int i=0;i<9;++i) printf("%lld ",ans[i]);
	printf("%lld\n",ans[9]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/83449679