beautiful numbers树形dp or 数位dp
题目找链接
题意:
如果数a能被a中的每一位数整除(0除掉),则称a是一个beautiful number,求一个区间内的beautiful numbers的个数。
分析:
首先,很显然,l到r的所有beautiful numbers(以下写b n吧,有点长)等于0到r的b n个数减去0到l-1的b n的个数,这个不用证明吧。当然也很好证。然后我们只要能在时间允许的范围内求0到某个数的b n的数量就可以了。
怎么求能?我们把它转化到树上,先举个例子:如果要求0到23的b n的数量,那么我们假设有一个根为0的树,0这个结点有儿子0 1 2,然后0有儿子0 1 2 3 4 5 6 7 8 9,1有儿子0 1 2 3 4 5 6 7 8 9,2有儿子0 1 2 3,那么我们只要从根开始读,一直读到叶子节点(例如一直向最左走我们就读到000,一直向最右走就读到023,然后中间可以读到这两个数中间所有的数,不重且不漏),到每一个叶子节点记录一下这个数可不可以被经过的所有节点整除就好了,这不还是暴力吗。。。先别急,慢慢来,这样我们先想一下怎么转移,如果他能被x,y整除,一定能被其最小公倍数整除,当然,反过来说也成立,那我们只需要记录途径数字的最小公倍数就可以直接判断这样走的数能不能被所有位数整除。我们定义Dp[i][ha][k]表示所有过第i个节点的b n数(当然已经给节点编号),ha表示我们已经读到的数,于是我们有了转移方程:
Dp[i][ha][k]=sum{Dp[son[i][ha*10+val[son[i][j]]][j]][divisor(k,val[son[i][j]])]};
就不写公式了,大家应该可以看懂,divisor表示最小公倍数,val[i]表示第i个节点上写的数字,son[i][j]表示第i个节点的第j个儿子。
其实到这里,基本的思路就有了,但是解决问题还差一点,首先,这么大的数组开不下,然后就是时间复杂度接受不了。于是我们要进行优化。
其实大家会发现,很多棵子数是相似的,那是不是有些子树可以只求一边呢,当然可以,首先我们要明确一点,我们所有可能用到的k都有x%2520同余x(mod k),于是我们想一想,如果到某个节点读出的数字是0252025200,其实和读到0000252000(位数要相等)最后找到的状态数没什么不同(前题是两个节点的子树都是满的),那么我们就可以把ha表示为读到的数字%2520,于是此时你会发现,这个点的b n的数量和这个点的i只没有什么关系,所有这一维可以省掉,但是还有问题,就是虽然0000同余00000(mod 2520)但是并不等价,为什么呢,因为子树大小已经不一样了(及还需读的位数不一样),那么我们就可应再加一维表示还要读的数字的数量,然后进行记忆话搜索就可以了。
现在再重新说一下状态:Dp[i][j][k]表示还剩i个数字没有读,读到的数字数字%2520为j且走过的所有的数的最小公倍数为k读完满的子树之后可以找到b n的数量。注意,刚才我并没有对k进行描述,但现在有了,因为刚才k并不是状态,只是记录一下到这里的最小公倍数,也就是说可以没有k这一维。而这里我们不再区分节点,所有要记录k,k相同才等价。(要想明白是为什么等价)
说到这里,大家应该都知道该怎么做了但是Dp数组仍然有点大,还能不能缩小一下,可以:离散化,k可以取到的值其实是要小于2520的,我们只要找出可能取到的值就可以了。
还有就是当然边界特殊处理。
最后是他的时间复杂度,有人说是O(玄学)。。。其实还是有保证的,首先,Dp中每个值只会算一次,边界最多18层,每层最多有10个儿子,而儿子只有一个是边界,所有还是可以保证复杂度的。
部分细节见代码注释。
最后看代码吧:
#include <cstdio>
#include <cstring>
long long Dp[][][];
int mod=;//1-9的最小公倍数
long long Gcd(long long a,long long b){
if(!b)
return a;
return Gcd(b,a%b);
}
long long B(long long a,long long b){//求最小公倍数
if(!a||!b)//特殊处理0
return a+b;
return (a/Gcd(a,b))*b;
}
int w[];//记录每一位以处理边界
int li[];
long long dp(int len,int yu,int BB,bool bi){//开始dp
if(len==){//到达叶子节点
if(yu%BB==)
return ;
else
return ;
}
if(!bi&&Dp[len][yu][li[BB]]!=-)//已经有等价情况处理过了
return Dp[len][yu][li[BB]];
int j;
if(bi)
j=w[len];
else
j=;
long long ans=;
for(int i=;i<j;i++)
ans+=dp(len-,(yu*+i)%mod,B(BB,i),);
ans+=dp(len-,(yu*+j)%mod,B(BB,j),bi);
if(!bi)
Dp[len][yu][li[BB]]=ans;
return ans;
}
long long Cl(long long a){
int len=;
while(a){
len++;
w[len]=a%;
a/=;
}
return dp(len,,,);//算出位数进行处理
}
int main(){
memset(Dp,-,sizeof(Dp));//0可能会tle,因为可能有0的一些节点要算好多遍
int t;
scanf("%d",&t);
int al=;
for(int i=;i<=;i++)//离散化
if(%i==){
al++;
li[i]=al;
}
for(int i=;i<=t;i++){
long long l,r;
scanf("%I64d%I64d",&l,&r);//题目说不用%lld
printf("%I64d\n",Cl(r)-Cl(l-));
}
return ;
}
beautiful numbers树形dp or 数位dp的更多相关文章
- BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP
BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺 ...
- 【BZOJ】1662: [Usaco2006 Nov]Round Numbers 圆环数(数位dp)
http://www.lydsy.com/JudgeOnline/problem.php?id=1662 这道题折腾了我两天啊-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 果然 ...
- 数位dp初步——数位dp的两种方式
数位dp:一类统计区间[L,R]内某种符合规定的数字个数的题目.特征是R的范围会很大,O(N)范围内无法完成. 一般而言,解决这类题目有两种方式,一种是递推,另一种是记忆化搜索. 递推: 1)利用dp ...
- 区间DP,数位DP
dp(动态规划)顾名思义便是动态的一种规划,而这种规划往往会跟状态,状态转移方程,记忆化搜索扯上关系,当然DP也是各个OI考试的必考点和常考点,在毒瘤出题人的折磨下,出现了许许多多的动态规划,有线性, ...
- BestCoder Round #75 King's Order dp:数位dp
King's Order Accepts: 381 Submissions: 1361 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 655 ...
- CF908G New Year and Original Order(DP,数位 DP)
又一次降智…… (数位 DP 原来可以写这么短,学到了) 问题可以转化为求数位中 $\ge k$ 的有恰好 $j$ 位的数的个数.设为 $c_{j,k}$. 那么答案就是:(考虑把 $k$ 的贡献拆开 ...
- [Codefroces401D]Roman and Numbers(状压+数位DP)
题意:给定一个数,求将该数重新排列后mod m==0的方案数 重新排列就考虑到用到哪些数,以及此时mod m的值 于是dp[i][j]表示状态i中mod m==j的方案数 注意:转移的时候只要找到一种 ...
- SPOJ BALNUM Balanced Numbers 平衡数(数位DP,状压)
题意: 平衡树定义为“一个整数的某个数位若是奇数,则该奇数必定出现偶数次:偶数位则必须出现奇数次”,比如 222,数位为偶数2,共出现3次,是奇数次,所以合法.给一个区间[L,R],问有多少个平衡数? ...
- [转]数位dp小记
转载自:http://blog.csdn.net/guognib/article/details/25472879 参考: http://www.cnblogs.com/jffifa/archive/ ...
随机推荐
- 96题--不同的二叉搜索树(java、中等难度)
题目描述:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例如下: 分析:本题可用动态规划的方法求解. 设 dp[n] 表示以 1 ... n 为节点组成的二叉搜索树的种类 ...
- Python常用推导式
列表推导式: #列表推导式 #基本格式[] # 变量 = [for循环的变量 for循环一个可迭代对象] # 变量 = [i for i in 可迭代对象 if 条件] 条件为true才进行appen ...
- MacOS配置.bash_profile,重启终端后配置失效和MacOS .zshrc does not exist问题
MacOS配置.bash_profile,重启终端后配置失效和MacOS .zshrc does not exist问题 场景 在Mac中配置golang环境变量更改GOPATH路径,在~/.ba ...
- vue2.0+Element UI 表格前端分页和后端分页
之前写过一篇博客,当时对element ui框架还不太了解,分页组件用 html + css 自己写的,比较麻烦,而且只提到了后端分页 (见 https://www.cnblogs.com/zdd20 ...
- [转] C++中的namespace
点击阅读原文 namespace中文意思是命名空间或者叫名字空间,传统的C++只有一个全局的namespace,但是由于现在的程序的规模越来越大,程序的分工越来越细,全局作用域变得越来越拥挤,每个人都 ...
- OSI模型各层详解
1. OSI概述 1.1 模拟器说明 1.1.1 模拟器的作用 搭建实验环境进行测试. 1.1.2 模拟器的类型 PT:一般是学校中使用,命令不完整,且不能抓包 GNS3:思科(CCNA,CCNP), ...
- Dos命令提示符下 - 用sqlcmd执行*.sql语句
Dos命令提示符下 - 用sqlcmd执行*.sql语句 1)在Dos命令下执行sqlcmd命令(当然事先需要将sqlcmd增加到环境变量中去), 2)下面白色部分替换为服务器名或计算机名即可sqlc ...
- Java中泛型的继承
最新在抽取公共方法的时候,遇到了需要使用泛型的情况,但是在搜索了一圈之后,发现大部分博客对于继承都说的不太清楚,所幸还有那么一两篇讲的清楚的,在这里自己标记下. 以我自己用到的代码举例,在父类中使用了 ...
- JavaWeb网上图书商城完整项目--24.注册页面的css样式实现
现在框架已经做好了,即下来我们要对页面进行装饰了,第一步给每一个元素添加id 1.最外面的div添加id为divMain 2.第二个div添加id为divTitle,里面的span对应的id为span ...
- 从 Tapable 中得到的启发
Tapable Why Tapable 前端开发中 Webpack 本质上是基于事件流的运行机制,它的工作流程是将特定的任务分发到指定的事件钩子中去完成.而实现这一切的核心就是 tapable,Web ...