[HAOI2010]计数 数位DP+组合数
题面:
你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。
现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).
样例输入:1020 样例输出:7
题解:
刚看到这道题的时候有点懵,,,,,
其实仔细观察一下发现这题可以用组合做。
注意到0的个数是不限的,
而且如果位数小于给定n的话,肯定可以随便搭配,
所以直接加上给定数的有效位+补全的0的全排列就好了,(全排列注意不能重复)
有效位指的是不为0的位,
这样虽然有前导0,但是可以看做是位数少的数,比如
0012,0120分别可以看做12和120,
这样不仅不用去掉前导0,还可以只用加一次,非常方便
因此我们要求就只剩下位数相同的方案了,
这就要用到数位DP了
我们从前往后枚举,因为如果确定的当前位小于给定n的当前位的话,
由于是从前往后枚举,只要当前位小了,后面的位不管怎么排列都是小于n的,
因此我们加上去掉已经被确定的数后的全排列(全排列不能重复)就可以了,
如果当前位相同,那方案数就要放在后面求(注意每确定一位都要在有的数集合中减去它)
注意在第一位的时候不要枚举0就可以了
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 60 #define LL long long
LL len,ans,tot;
int num[AC],have[AC],use[AC];
/*第i位是什么,在哪里?
设给定n的位数为len,那么位数小于len的显然可以随意组合,
于是利用有效位组合计算。
位数大于len的显然不合法
所以只要计算位数等于len的即可,
假设n的第i位不为0,那么如果用比第i位小的数摆在第i位上,
这个数就肯定比n小了,所以后面就可以随意搭配了
所以利用组合来算?
之前只想到位数小于len的可以组合算,
原来确定了前面的后面也可以组合算啊(废话),
如果g[i]表示i位数,自由组合的合法方案,i>=有效位
则g[i] = i! - (i-1)!
看上去怎么这么玄啊。。。。
果然是错的,,,因为如果有效位中有重复的话,这一部分就会被重复计算,
比如26637,那么对于每一种合法排列,都会计算2次,因为6可以交换
所以要去重,如果数i的出现次数为have[i],那么它会使得整个数列被多计算have[i]!次,
同理,拓展到所有数,整个数列会被多计算have[i]! * have[i+1]! + .......
因此设f[i]表示长度为i时的排列(无重复,有前导零)
g[i]表示长度为i时的排列(无重复,无前导零)
那么g[i] = f[i] - f[i-1];//相当于确定了第1位是0时,就必然有前导零了.
设比当前位i大的数有k位,
那么ans += k * newf[n-i];
因为这时前几位已经确定,所以have和tot都变了,因此要用newf,而不是f
因此可以发现,前面求的g和f到后面根本就没有用,所以还是不要求了,,,,,
直接算就好了
ans += tot!/have[1]! * have[2]!.....
*/
void pre()
{
char c=getchar();
while(c > '' || c < '') c=getchar();
while(c >= '' && c <= '')
{
num[++len]=c - '',c=getchar();
if(num[len] != ) ++have[num[len]],++tot;//统计有效位
}//读入
have[]=len - tot - ;//获取0的个数
if(!tot)
{
printf("0\n");
exit();
}
} LL cal(int cnt)
{
LL tmp=;
/* for(R i=1;i<=9;i++) tmp+=have[i],use[i]=have[i];
use[0]=cnt - tmp;//获取0的个数,因为work中获取了,所以不用再次获取*/
for(R i=;i<=;i++) use[i] = have[i];
tmp=;
for(R i=;i<=cnt;i++)
{
tmp*=i;
for(R j=;j<=;j++)
{
if(use[j] <= ) continue;
while(!(tmp % use[j]))
{
tmp /= use[j];
--use[j];
if(use[j] == ) break;
}
}
}
return tmp;
} void work()
{
LL tmp;
have[]++;//把之前为了计算小于len位的答案而减掉的0加回来
for(R i=;i<=len;i++)
{
tmp=;
for(R j=;j<=;j++) tmp+=have[j];
have[]=(len - i + ) - tmp;//获取0的个数
int b=(i == );//因为第一位不能用0代替
for(R j=b;j<num[i];j++)//枚举放哪个,但只能尝试比当前位小的,且这样还可以保证不重复
{
if(!have[j]) continue;//没有就不能放
--have[j];//先减掉
/*if(i == 1)//error!!!但是只要第一个不是0的话,,,,后面的就不是前导零啊,,,有什么关系。。。。
{//求g值,如果第一个是0的话,其他的have不会变,have[0]又是临时求的,所以没关系
ans += cal(len - i);
--have[0];
ans -= cal(len - i -1);
++have[0];
}*/
ans+=cal(len - i);//只有第一位才不能有前导0,,,,
++have[j];
}
if(num[i]) have[num[i]]--;//假设这一位也相同,那么就要减掉,0的时候不用减,因为0是临时算的(不过应该也可以不临时算?)
}
printf("%lld\n",ans);
} int main()
{
// freopen("in.in","r",stdin);
pre();
if(len - >= tot) ans+=cal(len-);//这样会有前导零,但可以看做是位数不同
work();
// fclose(stdin);
return ;
}
[HAOI2010]计数 数位DP+组合数的更多相关文章
- BZOJ2425:[HAOI2010]计数(数位DP)
Description 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1 ...
- BZOJ 2425 [HAOI2010]计数:数位dp + 组合数
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2425 题意: 给你一个数字n,长度不超过50. 你可以将这个数字: (1)去掉若干个0 ( ...
- 【BZOJ-1833】count数字计数 数位DP
1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 2494 Solved: 1101[Submit][ ...
- bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)
1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义 ...
- 【题解】P2602 数字计数 - 数位dp
P2602 [ZJOI2010]数字计数 题目描述 给定两个正整数 \(a\) 和 \(b\) ,求在 \([a,b]\) 中的所有整数中,每个数码(digit)各出现了多少次. 输入格式 输入文件中 ...
- BZOJ 1833 数字计数 数位DP
题目链接 做的第一道数位DP题,听说是最基础的模板题,但还是花了好长时间才写出来..... 想深入了解下数位DP的请点这里 先设dp数组dp[i][j][k]表示数位是i,以j开头的数k出现的次数 有 ...
- bzoj1833: [ZJOI2010]count 数字计数 数位dp
bzoj1833 Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. O ...
- NEUQ OJ 2004:追梦之人 (计数数位dp)
2004: 追梦之人 描述 题目描述: 为了纪念追梦人,粉丝们创造了一种新的数——“追梦数”.追梦数要满足以下两个条件:1.数字中不能出现“7”2.不能被7整除.比如:777和4396就不是追梦数,而 ...
- LightOJ 1140 计数/数位DP 入门
题意: 给出a,b求区间a,b内写下过多少个零 题解:计数问题一般都会牵扯到数位DP,DP我写的少,这道当作入门了,DFS写法有固定的模板可套用 dp[p][count] 代表在p位 且前面出现过co ...
随机推荐
- maven私有库搭建
一.在企业中基本上都会有自己的maven私有库,主要的目的就是方便依赖包的下载.如果采用远程的方式来实现的话,很多时候会考虑网速问题.如果自己活着公司搭建的私有库,这一样在使用上面会效率更高. 二.私 ...
- dubbo之main启动
一.dubbo的main启动在使用上面会简单的多,但是需要做一些简单的配置. dubbo.spring.config=classpath*:META-INF/spring/*.xml 备注:这个是默认 ...
- springboot与activemq的使用
1.springboot和activemq的使用相对来说比较方便了,我在网上看了很多其他的资料,但是自己写出来总是有点问题所以,这里重点描述一下遇到的一些问题. 2.至于activemq的搭建和spr ...
- 说说CakePHP的关联模型之一 基本关联
一个无论多么复杂的程序,拆开看无非是三种逻辑结构的组合:顺序结构.条件结构和循环结构. 类似的,数据库中表与表的之间的关联无外乎四种:一对一.一对多.多对一和多对多. CakePHP的模型层中定义了四 ...
- 卷积神经网络CNN在自然语言处理中的应用
卷积神经网络(Convolution Neural Network, CNN)在数字图像处理领域取得了巨大的成功,从而掀起了深度学习在自然语言处理领域(Natural Language Process ...
- Ruby 基础教程1-4
1.对象 数值对象 字符串对象 数组对象,散列对象 正则表达式对象 时间对象 文件对象 符号对象 2.类 Numeric String Array Hash Regexp File Symbol 3. ...
- 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)
题意与分析(CodeForces 540D) 是一道概率dp题. 不过我没把它当dp做... 我就是凭着概率的直觉写的,还好这题不算难. 这题的重点在于考虑概率:他们喜相逢的概率是多少?考虑超几何分布 ...
- 剑指offer-顺时针打印矩阵19
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- 文本分类-TextCNN
简介 TextCNN模型是由 Yoon Kim提出的Convolutional Naural Networks for Sentence Classification一文中提出的使用卷积神经网络来处理 ...
- DataTable转Json,Json转DataTable
// 页面加载时 /// </summary> /// <param name="sender"></param> /// <param ...