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;
}