数位DP入门:(bzoj1833+3209)
2014-12-13 22:22
127 查看
//我是来看文章创建时间的= =
膜拜了一下蔡大神。。。。
人生第一道自己写的数位DP。。。好吧以前是看题解然后也不知道为什么就过了的>_< 虽然说现在还是只会裸题= =
数位DP介绍:
http://wenku.baidu.com/link?url=9OS5Ybpw5wx00ahrH8ED2oyIlR1uWwrxT8N4pEg27GgBt2T2hLe4sd_h1rmpY7P0HmeHIEDw9h6_K98dPhhjoMhD2TpKcS8w1X8cC_dkPp_
接下来是bzoj1833题目地址:
http://www.lydsy.com:808/JudgeOnline/problem.php?id=1833
题意挺明显。。
首先我萌可以用F[i,j,k]表示有i位数字且前导为k的数中总共含有多少个数字j(0<=j<=9)。。。
然后蒟蒻不想太麻烦(太弱)。。。干脆把1~9拆成9次一次一次来。。。。
因为题目要求的是[l,r],所以答案就是[0,r]-[0,l-1]......
假设当前求的是0~x的数中含有多少个数字num。。。
首先设x有w位数字,把x拆成w位(第一位为最低位)存在数组h里面,然后处理出x的后i位凑起来是什么数(也就是x的w个后缀)
用pre[i]表示10^(i-1)...10^i-1中含有数字num的个数(这个数对于1~9都是一样的)
那么假设处理到第i(i从最高位(w)开始枚举)位:
1、先考虑之后位上存在num的情况:
当前位取的数字小于h[i]的时候,有h[i]种情况(0~h[i]-1),此时之后的所有数都可以选(之后不管选什么数都不超过x),所以总个数+=h[i]*pre[i-1](当前位每一种情况都能给总个数贡献之后位中数字个数)
当前位取的数字就是h[i]的时候呢?那就华丽地无视掉啦(为什么?想一想。)(后面的位就等到后面再处理了。。)
2、考虑当前位为num的情况:
如果num=h[i],由于之前位上的数字都是上限了,之后的数不能超出x的范围(若之前位数有任意一个小于上限,那么之后的数可以随便选,则已被上面的步骤计算到),所以能贡献出(当前再后一个后缀的值+1)个答案。。。。(因为还有一种情况是后面的全选0)
num>h[i],那么不论之后怎么取,当前这一位都不会额外贡献任何个数。。。(因为想要合法,则前面一定有数小于那一位的上限,贡献在之前就记算了)
num<h[i],那么当取的数字为num的时候后面的数可以随便取,且每种情况都会额外贡献一个个数(也就是当前位上的这个),所以总个数+=10^(i-1)
当然以上是针对数码1~9的情况。。。。如果要求0的个数的话目测比较麻烦。。。当然可以在预处理的时候再开一维pre[i,j]表示i位数字前导为j的个数。。。。。
当然我萌发现,0出现的次数就是所有数字次数总和减去1~9的个数和= =
蒟蒻扯半天最终确定自己语死早了>_<。。。。
下面是更丑的代码TAT
View CodeII
膜拜了一下蔡大神。。。。
人生第一道自己写的数位DP。。。好吧以前是看题解然后也不知道为什么就过了的>_< 虽然说现在还是只会裸题= =
数位DP介绍:
http://wenku.baidu.com/link?url=9OS5Ybpw5wx00ahrH8ED2oyIlR1uWwrxT8N4pEg27GgBt2T2hLe4sd_h1rmpY7P0HmeHIEDw9h6_K98dPhhjoMhD2TpKcS8w1X8cC_dkPp_
接下来是bzoj1833题目地址:
http://www.lydsy.com:808/JudgeOnline/problem.php?id=1833
题意挺明显。。
首先我萌可以用F[i,j,k]表示有i位数字且前导为k的数中总共含有多少个数字j(0<=j<=9)。。。
然后蒟蒻不想太麻烦(太弱)。。。干脆把1~9拆成9次一次一次来。。。。
因为题目要求的是[l,r],所以答案就是[0,r]-[0,l-1]......
假设当前求的是0~x的数中含有多少个数字num。。。
首先设x有w位数字,把x拆成w位(第一位为最低位)存在数组h里面,然后处理出x的后i位凑起来是什么数(也就是x的w个后缀)
用pre[i]表示10^(i-1)...10^i-1中含有数字num的个数(这个数对于1~9都是一样的)
那么假设处理到第i(i从最高位(w)开始枚举)位:
1、先考虑之后位上存在num的情况:
当前位取的数字小于h[i]的时候,有h[i]种情况(0~h[i]-1),此时之后的所有数都可以选(之后不管选什么数都不超过x),所以总个数+=h[i]*pre[i-1](当前位每一种情况都能给总个数贡献之后位中数字个数)
当前位取的数字就是h[i]的时候呢?那就华丽地无视掉啦(为什么?想一想。)(后面的位就等到后面再处理了。。)
2、考虑当前位为num的情况:
如果num=h[i],由于之前位上的数字都是上限了,之后的数不能超出x的范围(若之前位数有任意一个小于上限,那么之后的数可以随便选,则已被上面的步骤计算到),所以能贡献出(当前再后一个后缀的值+1)个答案。。。。(因为还有一种情况是后面的全选0)
num>h[i],那么不论之后怎么取,当前这一位都不会额外贡献任何个数。。。(因为想要合法,则前面一定有数小于那一位的上限,贡献在之前就记算了)
num<h[i],那么当取的数字为num的时候后面的数可以随便取,且每种情况都会额外贡献一个个数(也就是当前位上的这个),所以总个数+=10^(i-1)
当然以上是针对数码1~9的情况。。。。如果要求0的个数的话目测比较麻烦。。。当然可以在预处理的时候再开一维pre[i,j]表示i位数字前导为j的个数。。。。。
当然我萌发现,0出现的次数就是所有数字次数总和减去1~9的个数和= =
蒟蒻扯半天最终确定自己语死早了>_<。。。。
下面是更丑的代码TAT
var c:array[0..50,0..50]of int64; i,j,k:longint; n,m,ans,bilibili,temp:int64; num:array[0..50]of int64; function qpow(a,b:int64):int64; var date:int64; begin date:=1; while b>0 do begin if b and 1=1 then date:=date*a mod bilibili; b:=b shr 1; if b>0 then a:=a*a mod bilibili end; exit(date) end; begin bilibili:=10000007; readln(n); m:=trunc(ln(n)/ln(2))+1; for i:=0 to m do c[i,0]:=1; for j:=1 to m do for i:=1 to m do c[i,j]:=c[i-1,j]+c[i-1,j-1]; i:=0; while n>0 do begin inc(i); num[m-i+1]:=n and 1; n:=n shr 1 end; ans:=1; for i:=1 to m do if num[i]=1 then begin for j:=1 to m-i do ans:=(ans*qpow(temp+j,c[m-i,j]))mod bilibili; ans:=ans*(temp+1) mod bilibili; inc(temp) end; writeln(ans) end.
View CodeII
相关文章推荐
- 数位dp入门
- 数位dp总结 之 从入门到模板
- 数位DP入门篇之HDU2089——不要62
- 数位dp 入门 nbut 1475
- 数位DP入门题
- 数位dp总结 之 从入门到模板
- Hibernate 入门
- 转:关于Struts的几个入门问题
- C# 特性(Attribute)入门(二)
- Android模拟器入门
- EFI/UEFI BIOS 入门--转载
- EMIPLIB使用入门
- chrome浏览器插件开发入门
- 遗传算法入门
- 图数据库 Neo4j 入门
- Spring MVC 教程,快速入门,深入分析[1-11]
- 易语言入门教程之屏幕输出程序
- Linux上ClearSilver的使用入门
- cocosbuilder 入门1