题目找链接

题意:

  如果数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的更多相关文章

  1. BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP

    BZOJ_1662_[Usaco2006 Nov]Round Numbers 圆环数_数位DP Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺 ...

  2. 【BZOJ】1662: [Usaco2006 Nov]Round Numbers 圆环数(数位dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1662 这道题折腾了我两天啊-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 果然 ...

  3. 数位dp初步——数位dp的两种方式

    数位dp:一类统计区间[L,R]内某种符合规定的数字个数的题目.特征是R的范围会很大,O(N)范围内无法完成. 一般而言,解决这类题目有两种方式,一种是递推,另一种是记忆化搜索. 递推: 1)利用dp ...

  4. 区间DP,数位DP

    dp(动态规划)顾名思义便是动态的一种规划,而这种规划往往会跟状态,状态转移方程,记忆化搜索扯上关系,当然DP也是各个OI考试的必考点和常考点,在毒瘤出题人的折磨下,出现了许许多多的动态规划,有线性, ...

  5. BestCoder Round #75 King&#39;s Order dp:数位dp

    King's Order Accepts: 381 Submissions: 1361 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 655 ...

  6. CF908G New Year and Original Order(DP,数位 DP)

    又一次降智…… (数位 DP 原来可以写这么短,学到了) 问题可以转化为求数位中 $\ge k$ 的有恰好 $j$ 位的数的个数.设为 $c_{j,k}$. 那么答案就是:(考虑把 $k$ 的贡献拆开 ...

  7. [Codefroces401D]Roman and Numbers(状压+数位DP)

    题意:给定一个数,求将该数重新排列后mod m==0的方案数 重新排列就考虑到用到哪些数,以及此时mod m的值 于是dp[i][j]表示状态i中mod m==j的方案数 注意:转移的时候只要找到一种 ...

  8. SPOJ BALNUM Balanced Numbers 平衡数(数位DP,状压)

    题意: 平衡树定义为“一个整数的某个数位若是奇数,则该奇数必定出现偶数次:偶数位则必须出现奇数次”,比如 222,数位为偶数2,共出现3次,是奇数次,所以合法.给一个区间[L,R],问有多少个平衡数? ...

  9. [转]数位dp小记

    转载自:http://blog.csdn.net/guognib/article/details/25472879 参考: http://www.cnblogs.com/jffifa/archive/ ...

随机推荐

  1. 原生js实现图片瀑布流布局,注释超详细

    完整代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  2. mtail 调试

    mtail 调式 mtail 不会采集当前accesslog 内容以前的内容,只有当你启动mtail后,去访问你的监控tomcat,有新的access 日志刷入localhost_access_log ...

  3. 如何在VMware中进行创建CentOS虚拟机

    今天给大家分享如何在VMware中创建CentOS虚拟机,CentOS6.7为例进行说明,CentOS7版本亦可以参考该教程,具体的教程如下. 1.之后打开VMware,主页面如下图所示.点击第一个框 ...

  4. MySQL连接查询驱动表被驱动表以及性能优化

    准备我们需要的表结构和数据 两张表 studnet(学生)表和score(成绩)表, 创建表的SQL语句如下 CREATE TABLE `student` ( `id` int(11) NOT NUL ...

  5. Spark原始码系列(五)分布式缓存

    问题导读:spark缓存是如何实现的?BlockManager与BlockManagerMaster的关系是什么? 这个persist方法是在RDD里面的,所以我们直接打开RDD这个类. def pe ...

  6. (十)深入理解maven构建生命周期和各种plugin插件

    链接:https://blog.csdn.net/zhaojianting/article/details/80321488

  7. MySQL——事务(Transaction)详解

    原文:https://blog.csdn.net/w_linux/article/details/79666086

  8. numpy(深)复制一个矩阵的方法

    在用Python写代码的时候往往会遇到真复制和假复制的问题,真复制就是创建一个新的实例(instance),而假复制就是把原对象的引用赋给了新的标志符.判断是不是真复制可以使用id()这个函数. 当然 ...

  9. 关于安装Django包的问题

    在Windows的环境下,有些包确实不好安装的,比如reportlab-3.2.0-cp27-none-win32.whl,根据xadmin安装的经验,从这个.whl里把文件夹reportlab解压出 ...

  10. 分布式系统框架Spring+Redis+SSO视频课程

    1.视频讲解的参看博客 这应该是第一个简单的分布式系统soa入门的基础,视频中对sao面向服务编程讲解的很透彻,第redis缓存讲解的也比较清楚,讲解了sso单点登录使用token的方式,还有cas实 ...