BZOJ 2425 [HAOI2010]计数:数位dp + 组合数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2425

题意:

  给你一个数字n,长度不超过50。

  你可以将这个数字:

    (1)去掉若干个0

    (2)打乱后重新排列

  问你可以产生多少个小于n的数字。

题解:

  题目中的第一个操作其实是没有用的。

  去掉若干个0之后再重新排列(不允许前导0),和不去0直接重新排列(允许前导0),其实是等价的。

  所以按照数位dp的方法从高到低按位统计。

  如n = 2345时,分别统计前缀为0~1, 20~22, 230~233, 2340~2344的答案。

  最高位为第1位。

  假设当前考虑到第i位,1~i-1位都和原数字n完全匹配。

  枚举第i位可以填了x∈[0,a[i]),则先让cnt[x]--。

  然后就是i+1位之后的数如何填了。

  设len = n-i。

  方案数 = 先从len个位置中找了cnt[0]个位置全填0的方案数 * 又从(len-cnt[0])个位置中找了cnt[1]个位置全填1的方案数...

  方案数 = C(len,cnt[0]) * C(len-cnt[0],cnt[1]) * C(len-cnt[0]-cnt[1],cnt[2])...

  最后再让cnt[x]++回来,然后cnt[a[i]]--就好了。

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX_N 55
 5 #define MAX_D 15
 6
 7 using namespace std;
 8
 9 int n;
10 long long ans=0;
11 long long a[MAX_N];
12 long long cnt[MAX_N];
13 long long c[MAX_N][MAX_N];
14 char s[MAX_N];
15
16 void read()
17 {
18     scanf("%s",s+1);
19     n=strlen(s+1);
20     for(int i=1;i<=n;i++) cnt[a[i]=s[i]-‘0‘]++;
21 }
22
23 void cal_c()
24 {
25     c[0][0]=1;
26     for(int i=1;i<=n;i++)
27     {
28         c[i][0]=1;
29         for(int j=1;j<=i;j++)
30         {
31             c[i][j]=c[i-1][j]+c[i-1][j-1];
32         }
33     }
34 }
35
36 long long cal_p(int len)
37 {
38     long long now=1;
39     for(int i=0;i<=9;i++)
40     {
41         now*=c[len][cnt[i]];
42         len-=cnt[i];
43     }
44     return now;
45 }
46
47 void cal_ans()
48 {
49     for(int i=1;i<=n;i++)
50     {
51         for(int j=0;j<a[i];j++)
52         {
53             cnt[j]--;
54             ans+=cal_p(n-i);
55             cnt[j]++;
56         }
57         cnt[a[i]]--;
58     }
59 }
60
61 void work()
62 {
63     cal_c();
64     cal_ans();
65     printf("%lld\n",ans);
66 }
67
68 int main()
69 {
70     read();
71     work();
72 }

原文地址:https://www.cnblogs.com/Leohh/p/8547392.html

时间: 2024-08-02 15:01:51

BZOJ 2425 [HAOI2010]计数:数位dp + 组合数的相关文章

[HAOI2010] 计数 - 数位dp,组合数

你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等. 现在给定一个数,问在这个数之前有多少个数.(注意这个数不会有前导0). Solution 可重复康托展开 常用数位 dp 套路,枚举哪一位开始比原数小,前方唯一而后方算全排列 回避高精度的全排列数计算,考虑到阶乘最多大约算到 50, 开个因子计数桶,然后在桶上操作就可以了 #include <bits/st

BZOJ 1833 数字计数(数位DP)

经典数位DP模板题. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath>

bzoj 2425 [HAOI2010]计数 数学

题面 题目传送门 解法 显然可以一位一位确定答案 假设在第\(i\)时已经比原数小了,那么答案就可以加上\(\frac{(n-i)!}{s_0!s_1!-s_9!}\) 但是因为\(n≤50\),所以可能会爆long long,需要高精度 然而我就十分sb地写了高精度-- 其实并不需要用高精度啊 可以先确定0放置的方案,在剩下的里面1的方案,-- 然后对这个东西不断乘起来就好了,就是一个组合数的问题 代码(sb的高精度) #include <bits/stdc++.h> using names

BZOJ 1833 ZJOI 2010 count 数字计数 数位DP

题目大意:问0~9这10个数字在[l,r]中出现过多少次. 思路:数位DP.以前只是听说过,并没有写过,写了才发现好闹心啊.. 预处理一个数组,f[i][j][k]表示长度为i,开头为j,数字k出现的次数. 对于一个数kXXXXXX,我们先处理1~999999,然后处理1000000~kXXXXXX 前面的东西很规则,可以直接调用f数组来解决. 对于后面不太规则的东西,按位处理.总之就是乱搞,我也不太懂说不明白... CODE: #include <cstdio> #include <c

【BZOJ-1833】count数字计数 数位DP

1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2494  Solved: 1101[Submit][Status][Discuss] Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. Output 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次.

bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义f[i][j][k]表示若前i个位置有k个j的此时的全局方案数,然后就可以记忆化搜索了(具体看代码吧) 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath>

BZOJ 1799 同类分布(数位DP)

给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数.1<=a<=b<=1e18. 注意到各位数字之和最大是153.考虑枚举这个东西.那么需要统计的是[0,a-1]和[0,b]内各位数字之和为x且能整除x的数字个数. 那么我们只需要数位dp一波即可. 令dp[pos][i][x]表示有pos位且数字之和为x的数mod P=i的数字个数. 则转移方程显然可得. # include <cstdio> # include <cstring> # include

bzoj 1026 windy数(数位DP)

1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 2615  Solved: 1155 [Submit][Status] Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A B. Output 一个整数. Sample Input [

BZOJ1833: [ZJOI2010]count 数字计数 (数位dp)

传送门 数位dp... ...大概都是这个套路吧... ... 写这个的时候直接水了一发... ...我也不知道自己写的是不是dp... ... 大概是主要内容和dp关系不大的dp... ... mark代码..细长的代码真是丑啊..换行太频繁了.... 1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm&g