BZOJ2425 [HAOI2010]计数 【数位dp】

题目

你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。

现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).

输入格式

只有1行,为1个整数n.

输出格式

只有整数,表示N之前出现的数的个数。

输入样例

1020

输出样例

7

提示

n的长度不超过50,答案不超过\(2^{63}-1\).

题解

如果我们看做把0删除看做把0前导,那么问题就转化成了求所有数的排列中比当前数小的个数

我们只需统计当前\(i\)位相同,第\(i + 1\)位比原数小时有多少种情况

那么剩余的位就可以随便排列了,用带重复元素的排列\(\frac{N!}{n1!*n2!*n3!......}\)

当然可能会爆long long,可以对阶乘质因子分解来计算

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 55,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int num[maxn],n,isn[maxn];
LL a[10],fac[maxn],p[maxn],pi,ans;
void init(){
    for (int i = 2; i < maxn; i++){
        if (!isn[i]) p[++pi] = i;
        for (int j = 1; j <= pi && i * p[j] < maxn; j++){
            isn[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}
LL Cal(LL x,LL t){
    LL re = 0;
    while (x / t) re += (x /= t);
    return re;
}
LL qpow(LL a,LL b){
    LL re = 1;
    for (; b; b >>= 1,a = a * a)
        if (b & 1) re = re * a;
    return re;
}
LL cal(){
    LL re = 1,tot = 0;
    for (int i = 0; i < 10; i++) tot += a[i];
    for (int i = 1; i <= pi && p[i] <= tot; i++){
        LL cnt = Cal(tot,p[i]);
        for (int j = 0; j < 10; j++) cnt -= Cal(a[j],p[i]);
        re = re * qpow(p[i],cnt);
    }
    return re;
}
int main(){
    init();
    char c;
    while ((c = getchar()) != EOF){
        if (!isdigit(c)) break;
        num[++n] = c - ‘0‘;
        a[num[n]]++;
    }

    for (int i = 1; i <= n; i++){
        for (int j = 0; j < num[i]; j++){
            if (!a[j]) continue;
            a[j]--;
            ans += cal();
            a[j]++;
        }
        a[num[i]]--;
    }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8734198.html

时间: 2024-11-10 08:17:05

BZOJ2425 [HAOI2010]计数 【数位dp】的相关文章

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

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

【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 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 数字计数(数位DP)

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

BZOJ2425 [HAOI2010]计数

比较简单的数位dp,但是要用到组合公式C,预处理吧... 1 /************************************************************** 2 Problem: 2425 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:0 ms 7 Memory:828 kb 8 ***********************************************************

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

【BZOJ1833】【ZJOI2010】数字计数 数位DP

链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/46444975"); } 题解: 然而并没有DP. [1,R]的答案减去[1,L]的答案. 对于一个数 X ,求 [1,X] 的答案,我是先处理出 [1,999--9] 的答案(那个999--9 < X) 然后按

题解——[ZJOI2010]数字计数 数位DP

最近在写DP,今天把最近写的都放上来好了,,, 题意:给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. 首先询问的是一个区间,显然是要分别求出1 ~ r ,1 ~ l的答案,然后相减得到最终答案 首先我们观察到,产生答案的区间是连续的,且可以被拆分, 也就是说0 ~ 987的贡献= 0 ~ 900 + 901 ~ 987的恭喜, 同理,把位拆开也是等价的,所以我们可以单独计算每个位的贡献 这样讲可能有点不太清晰,举个例子吧 3872 我们先把它按数拆开来