首页 > 代码库 > POJ 2282 The Counting Problem,组合数学

POJ 2282 The Counting Problem,组合数学

POJ 2282 The Counting Problem,组合数学

ACM

题目地址:POJ 2282

题意

给出俩数n,m,求从n~m中0~9分别出现的次数。

分析

组合数学。 
只要能快速算出0~n中各个数的出现次数就能解决问题了。 
要把数拆开来看,比如3456=3000+400+50+6。 
然后就只要考虑后面都是0的数就行了。 
0~3000中,我们要分为两部分来考虑: 
在第一位中,0\1\2都出现了1000次。 
假设不管第一位,后面那些位数出现0~9的几率是均等的(先不考虑前导0)。那么就是0\1\2分别作为第一位,后面0~9出现的次数遍都能知道了。 
但是这样并不能直接去算后面的问题,因为第一位为3也出现了一些次数,也要算上去。 
各个位都能这样考虑。

代码

/*
*  Author:      illuz <iilluzen[at]gmail.com>
*  File:        2282.cpp
*  Create Date: 2014-06-04 09:24:27
*  Descripton:  comb 
*/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 10;

char s[N];
int a[N], b[N];
int n, m;

void calc(char *s, int *tab) {
	int len = strlen(s), t = 1;
	for (int i = 0; i < len - 1; i++) {
		t *= 10;
		tab[0]  -= t;	// 提前扣掉多算的0
	}
	for (int i = 0; i < len; i++) {
		int tmp = s[i] - '0';
		for (int j = 0; j < tmp; j++) {
			tab[j] += t;
		}
		for (int j = 0; j < 10; j++) {
			tab[j] += tmp * ((len - i - 1) * t / 10);
		}
		tab[tmp] += atoi(s + i + 1) + 1;
		t /= 10;
	}
}

int main() {
	while (~scanf("%d%d", &n, &m) && (n || m)) {
		memset(a, 0, sizeof(a));
		memset(b, 0, sizeof(b));
		if (n > m)
			swap(n, m);

		sprintf(s, "%d", n - 1);
		calc(s, a);
		sprintf(s, "%d", m);
		calc(s, b);
		for (int i = 0; i < N - 1; i++) {
			printf("%d ", b[i] - a[i]);
		}
		printf("%d\n", b[9] - a[9]);
	}
	return 0;
}