洛谷·bzoj·[ZJOI2010]数字计数

初见安~这里是两个传送门:洛谷P2602 & bzoj P1833

题目描述

给定两个正整数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。【反正我没看出来

但凡是这种求某个区间内的问题,就一定可以转化成f(r) - f(l - 1)的形式。所以我们只需要知道怎么求1~n中每个数字出现了多少次就行了

依照数位dp的套路思路,我们要先预处理一些东西,然后按照数位来分段处理。这个题就很明显,我们需要预处理的东西是f[ i ],即这个数在i位数中出现的次数。也很明显的——每个数的f[ i ]都一样。所以我们可以一劳永逸。直接用。

对于f[ i ]的预处理,我们易得一个递推式:f[i] = f[i - 1] * 10 + 10 ^{i - 1},前者为最高位为0~9时i - 1位中出现的次数,后者为这个数作为最高位时出现的次数。所以为了方便,我们也可以顺便把10的各个次幂处理出来,开个数组ten[ i ]表示10^i.。

而后就是核心代码——关于对n的分段。从高位到低位。

就比如58724,首先最高位为5,那么就说明前面的4位数满为0~9999循环了5次【最高位分别为0~4时】,所以我们将其分为49999+8725, 50000以内【1~49999】0~9每个数字都在后面的i - 1位里出现了5 * f[i - 1]次,最高位0~4分别都出现了10^4次。至此我们关于49999的操作就完成了。

而对于后一半8725,【注意此时我们的主体还是第i = 5位上的5】,在后面这8725个数中5作为最高位又出现了8725次,所以直接累加。至于8725中5出现了多少次,那是以后面的某一位为主体时的事情。

至此似乎就已经处理完了。但是数位dp有一个很大的问题需要处理——就是前导0。但是我们在使用f函数的时候分明最高位上都是合法的,并不影响啊。所以最后受到影响的只有0的个数。因为为了计算方便,我们将0和1~9一视同仁,但是在诸如0999,00099,00293…………之类的数中0都是多算了的。所以最后得出的答案里0的个数要减去10^0+10^1+...+10^{i - 1}个。【有个10^0=1是因为1~n不包含0这个数】,所以也就刚好顺应了我们的操作。

下面是代码及详解——

#include
using namespace std;
long long a, b, cnta[20], cntb[20];//一定要开long long!
long long f[20], ten[20];
void solve(long long n, long long *cnt)
{
	int num[20], tot = 0;
	while(n) {num[++tot] = n % 10; n /= 10;}
	
	for(int i = tot; i > 0; i--)
	{
		for(int j = 0; j <= 9; j++)//i - 1位中0~9出现次数更新
			cnt[j] += f[i - 1] * num[i];
		for(int j = 0; j < num[i]; j++)//最高位出现次数更新
			cnt[j] += ten[i - 1];
			
		long long num2 = 0;
		for(int j = i - 1; j > 0; j--)
			num2 = num2 * 10 + num[j];
		cnt[num[i]] += num2 + 1;
		
		cnt[0] -= ten[i - 1];//0的特殊处理
	}
}

int main()
{
	scanf("%lld%lld", &a, &b);
	ten[0] = 1;
	for(int i = 1; i <= 15; i++)
	{
		f[i] = f[i - 1] * 10 + ten[i - 1];
		ten[i] = ten[i - 1] * 10;
	}
	
	solve(a - 1, cnta);//记住要-1,否则算出来的就是a + 1~b了
	solve(b, cntb);
	for(int i = 0; i < 10; i++)
		printf("%lld ", cntb[i] - cnta[i]);
		
	return 0;
}

感觉这个题对数位dp的新人算是比较友好了:)

迎评:)
——End——

你可能感兴趣的:(动态规划)