[haoi2010]计数

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

基础数位dp之一;

普通dp思路:

首先想到设状态,怎么设呢?设的状态肯定要把前面用了多少数表示出来,按照这个思路就只能设f[i][j]表示在第i位,j是所有非0数字的二进制表示;

然后可以记忆化搜索进行转移;

这个方法可以过(HA的数据太水),但数据稍稍加强一下,时空全炸;

AC思路:

其实每个数都是原数的一个组合,那么我们自然可以用排列组合的方法计算;

思路还是逐位确定,从高位向低位确定数字,然后对下面的不确定的数进行排列组合,结果累积;

无论是空间还是时间都到了最优的程度;

第一种思路:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<ctime>
 7 #include<iomanip>
 8 #include<queue>
 9 #include<map>
10 #include<set>
11 #include<algorithm>
12 using namespace std;
13 #define FILE "dealing"
14 #define LL long long
15 #define up(i,j,n) for(int i=(j);i<=(n);i++)
16 void chkmin(int &a,int b){a>b?a=b:0;}
17 void chkmax(int &a,int b){a<b?a=b:0;}
18 namespace IO{
19     char buf[1<<15],*fs,*ft;
20     int gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1<<15,1,stdin),fs==ft))?-1:*fs++;}
21     int read(){
22         int x=0,f=0,ch=gc();
23         while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();}
24         while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();}
25         return f?-x:x;
26     }
27 }using namespace IO;
28 const int maxn=150;
29 int q[maxn],tail=0,pre[maxn];
30 char s[maxn],ch[maxn];
31 LL f[51][100000];
32 LL dfs(int pos,int num,int limit){
33     if(pos<1)
34         return num==0;
35     if(!limit&&f[pos][num]!=-1)return f[pos][num];
36     int len=limit?s[pos]:9;
37     LL ret=0;
38     ret+=dfs(pos-1,num,limit&&0==len);
39     up(i,1,len){
40         if(pre[i]==-1)continue;
41         up(j,pre[i],tail){
42             if(q[j]==i&&(num&(1<<j-1))){
43                 ret+=dfs(pos-1,num^(1<<j-1),limit&&i==len);
44                 break;
45             }
46         }
47     }
48     if(!limit)f[pos][num]=ret;
49     return ret;
50 }
51 int main(){
52     freopen(FILE".in","r",stdin);
53     freopen(FILE".out","w",stdout);
54     scanf("%s",ch+1);
55     int len=strlen(ch+1);
56     up(i,1,len)s[i]=ch[len-i+1]-‘0‘;
57     up(i,1,len)if(s[i])
58         q[++tail]=s[i];
59     sort(q+1,q+tail+1);
60     memset(pre,-1,sizeof(pre));
61     up(i,1,tail)
62         if(pre[q[i]]==-1)pre[q[i]]=i;
63     memset(f,-1,sizeof(f));
64     printf("%I64d\n",dfs(len,(1<<tail)-1,1)-1);
65     return 0;
66 }

第二种思路:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<ctime>
 7 #include<iomanip>
 8 #include<queue>
 9 #include<map>
10 #include<set>
11 #include<algorithm>
12 using namespace std;
13 #define FILE "dealing"
14 #define LL long long
15 #define up(i,j,n) for(int i=(j);i<=(n);i++)
16 void chkmin(int &a,int b){a>b?a=b:0;}
17 void chkmax(int &a,int b){a<b?a=b:0;}
18 namespace IO{
19     char buf[1<<15],*fs,*ft;
20     int gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1<<15,1,stdin),fs==ft))?-1:*fs++;}
21     int read(){
22         int x=0,f=0,ch=gc();
23         while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=1;ch=gc();}
24         while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=gc();}
25         return f?-x:x;
26     }
27 }using namespace IO;
28 const int maxn=52;
29 char s[maxn];
30 int a[10],len;
31 LL fac[30],c[maxn][maxn];
32 int main(){
33     freopen(FILE".in","r",stdin);
34     freopen(FILE".out","w",stdout);
35     c[0][0]=1;
36     for(int i=1;i<=50;i++){
37         c[i][0]=1;
38         for(int j=1;j<=50;j++)c[i][j]=c[i-1][j-1]+c[i-1][j];
39     }
40     scanf("%s",s+1);
41     len=strlen(s+1);
42     up(i,1,len)a[s[i]-‘0‘]++;
43     LL ans=0;
44     for(int i=1;i<=len;i++){
45         for(int j=0;j<s[i]-‘0‘;j++){
46             if(!a[j])continue;
47             a[j]--;
48             int now=len-i;
49             LL sum=1;
50             for(int k=0;k<=9;k++)
51                 sum*=c[now][a[k]],now-=a[k];
52             ans+=sum;
53             a[j]++;
54         }
55         a[s[i]-‘0‘]--;
56     }
57     cout<<ans<<endl;
58     return 0;
59 }

时间: 2024-11-10 08:38:22

[haoi2010]计数的相关文章

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

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

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 ***********************************************************

洛谷P2518 [HAOI2010]计数

题目:https://www.luogu.org/problemnew/show/P2518 题目描述 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等. 现在给定一个数,问在这个数之前有多少个数.(注意这个数不会有前导0). 输入输出格式 输入格式: 只有1行,为1个整数n. 输出格式: 只有整数,表示N之前出现的数的个数. 输入输出样例 输入样例#1:

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, 23

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前导,那么问题

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

P2518 [HAOI2010]计数

题目描述 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等. 现在给定一个数,问在这个数之前有多少个数.(注意这个数不会有前导0). 输入输出格式 输入格式: 只有1行,为1个整数n. 输出格式: 只有整数,表示N之前出现的数的个数. 输入输出样例 输入样例#1: 1020 输出样例#1: 7 说明 n的长度不超过50,答案不超过2^63-1. Soluti

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

数位dp初探

我这种蒟蒻就一直不会写数位dp.. 于是开了个坑.. 1833: [ZJOI2010]count 数字计数 这道被KPM大爷说是入门题..嗯似乎找找规律然后减掉0的情况后乱搞就可以了..(但是还是写了很久TAT #include<cstring> #include<iostream> #include<cstdio> #include<queue> #include<cmath> #include<algorithm> #define