[BZOJ2425][HAOI2010]计数(组合数学)
2017-10-29 22:00
357 查看
首先,统计出数字串中0,1,...,9出现的次数cnt[0],cnt[1],...,cnt[9]。
先考虑用其中的所有非零数字能构成多少个允许含前导0的len位数。
可以想到,现在len位中选出cnt[1]位设为1,然后在剩下的len−cnt[1]位中选出cnt[2]位设为2,再在剩下的len−cnt[1]−cnt[2]位中选出cnt[3]位设为3…,即结果为:
∏9i=1Ccnt[i]len−∑i−1j=1cnt[j]
而对于不含前导0的,就只要枚举最高位,就可以用上面的式子了(有些小细节,具体见代码实现)。
代码实现如下(lowe(len,0/1)表示构成len位数,第二个参数为0表示可以含前导0,为1表示不能含前导0):
继续考虑求位数相等且比原数字串小的数字串个数。
考虑最高位的取值,分两部分计算:
1、最高位的取值比原数字串的最高位小。这样后面位的值不管取多少,都是不会大于等于原数字串的。这时候可以用上面的方法求得。
2、最高位的取值等于原数字串的最高位。这时候就要求去掉最高位之后比原数字串小。但可以发现,这是和原问题相同的一个子问题,可以递归处理(在标程中使用的是非递归)。
注意判断前导0。
组合数可以使用公式Cmn=Cmn−1+Cm−1n−1预处理。
代码:
先考虑用其中的所有非零数字能构成多少个允许含前导0的len位数。
可以想到,现在len位中选出cnt[1]位设为1,然后在剩下的len−cnt[1]位中选出cnt[2]位设为2,再在剩下的len−cnt[1]−cnt[2]位中选出cnt[3]位设为3…,即结果为:
∏9i=1Ccnt[i]len−∑i−1j=1cnt[j]
而对于不含前导0的,就只要枚举最高位,就可以用上面的式子了(有些小细节,具体见代码实现)。
代码实现如下(lowe(len,0/1)表示构成len位数,第二个参数为0表示可以含前导0,为1表示不能含前导0):
ll lowe(int len, bool first) { int i, j; ll ans = 1; if (!first) { for (i = 1; i <= 9; len -= cnt[i++]) ans *= C[len][cnt[i]]; return ans; } ans = 0; for (i = 1; i <= 9; i++) { cnt[i]--; ans += lowe(len - 1, 0); cnt[i]++; } return ans; }
继续考虑求位数相等且比原数字串小的数字串个数。
考虑最高位的取值,分两部分计算:
1、最高位的取值比原数字串的最高位小。这样后面位的值不管取多少,都是不会大于等于原数字串的。这时候可以用上面的方法求得。
2、最高位的取值等于原数字串的最高位。这时候就要求去掉最高位之后比原数字串小。但可以发现,这是和原问题相同的一个子问题,可以递归处理(在标程中使用的是非递归)。
注意判断前导0。
组合数可以使用公式Cmn=Cmn−1+Cm−1n−1预处理。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1005, R = 12;
int n, num
, tot, cnt[R];
char s
; ll C
, Ans;
void init() {
int i, j; for (i = 0; i <= 1000; i++) C[i][0] = 1;
for (i = 1; i <= 1000; i++) for (j = 1; j <= i; j++)
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
ll lowe(int len, bool first) { int i, j; ll ans = 1; if (!first) { for (i = 1; i <= 9; len -= cnt[i++]) ans *= C[len][cnt[i]]; return ans; } ans = 0; for (i = 1; i <= 9; i++) { cnt[i]--; ans += lowe(len - 1, 0); cnt[i]++; } return ans; }
void solve() {
int i, j; for (i = 1; i <= n; i++) {
for (j = (i == 1); j < num[i]; j++) {
if (!cnt[j]) continue;
cnt[j]--; Ans += lowe(n - i, 0);
cnt[j]++;
}
cnt[num[i]]--;
}
}
int main() {
int i; scanf("%s", s + 1); n = strlen(s + 1);
for (i = 1; i <= n; i++) {
cnt[num[i] = s[i] - '0']++;
if (num[i]) tot++;
}
init(); for (i = tot; i < n; i++) Ans += lowe(i, 1);
cout << (solve(), Ans) << endl;
return 0;
}
相关文章推荐
- 【bzoj2425】【HAOI2010】【计数】【组合数学】
- bzoj2425 [HAOI2010]计数 组合数
- BZOJ 2111: [ZJOI2010]Perm 排列计数(简单组合数学)
- BZOJ2425: [HAOI2010]计数
- bzoj 2111: [ZJOI2010]Perm 排列计数 (组合数学+Lucas定理)
- BZOJ 2111: [ZJOI2010]Perm 排列计数|组合数学|Lucas定理|DP
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- [bzoj2111][ZJOI2010]Perm 排列计数(组合数学)
- [BZOJ2111][ZJOI2010]Perm 排列计数(组合数学+lucas定理)
- BZOJ2425 [HAOI2010]计数
- BZOJ2425 [HAOI2010]计数
- BZOJ2425: [HAOI2010]计数
- bzoj千题计划178:bzoj2425: [HAOI2010]计数
- [BZOJ2111][ZJOI2010]Perm排列计数(组合数学)
- BZOJ2425:[HAOI2010]计数——题解
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- BZOJ_4517_[Sdoi2016]排列计数_组合数学
- bzoj2111 Perm 排列计数 组合数学
- BZOJ 4517|SDOI 2016|排列计数|错排公式|组合数学
- [数学杂题]BZOJ 2111—— [ZJOI2010]Perm 排列计数