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; }
POJ 2282 The Counting Problem,组合数学,布布扣,bubuko.com
POJ 2282 The Counting Problem,组合数学
原文:http://blog.csdn.net/hcbbt/article/details/28395699