zoukankan      html  css  js  c++  java
  • 数位DP入门:(bzoj1833+3209)

    //我是来看文章创建时间的= =

    膜拜了一下蔡大神。。。。

    人生第一道自己写的数位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

       

     1 var
     2   pre,orz,h,sum:array[0..13]of int64;
     3   ans,ans1:array[0..9]of int64;
     4   i,j,k,n,m,mid:longint;
     5   l,r,tot,tot1,l1,r1,x,w1,w2:int64;
     6 procedure mahoshojo(x,num,w:int64);
     7 var i:longint;anss:int64;
     8 begin
     9   anss:=0;
    10   for i:=1 to w do h[i]:=x div orz[i-1] mod 10;
    11   for i:=1 to w do sum[i]:=x mod orz[i];
    12   for i:=w downto 1 do
    13   begin
    14     inc(anss,pre[i-1]*h[i]);
    15     if h[i]>num then inc(anss,orz[i-1]);
    16     if h[i]=num then inc(anss,sum[i-1]+1);
    17   end;
    18   ans[num]:=ans[num]+anss;
    19   tot:=tot-anss;
    20 end;
    21 begin
    22   pre[1]:=1;
    23   orz[0]:=1;
    24   for i:=1 to 13 do orz[i]:=orz[i-1]*10;
    25   for i:=2 to 13 do pre[i]:=pre[i-1]*10+orz[i-1];
    26   readln(l,r);
    27   dec(l);
    28   tot:=1;
    29   if l>0 then
    30   begin
    31     l1:=l;
    32     while l1>0 do begin inc(w1);l1:=l1 div 10 end;
    33     for i:=1 to w1-1 do inc(tot,(orz[i]-orz[i-1])*i);
    34     if w1>1 then  inc(tot,w1*(l-orz[w1-1]+1))  else tot:=l+1;
    35   end
    36   else
    37     w1:=1;
    38   r1:=r;
    39   while r1>0 do begin inc(w2);r1:=r1 div 10 end;
    40   tot1:=1;
    41   for i:=1 to w2-1 do inc(tot1,(orz[i]-orz[i-1])*i);
    42   if w2>1 then  inc(tot1,w2*(r-orz[w2-1]+1)) else tot1:=r+1;
    43 //  writeln(w2,':',tot1,'   ',w1,':',tot);
    44   for i:=1 to 9 do
    45   mahoshojo(l,i,w1);
    46   for i:=1 to 9 do ans[i]:=-ans[i];
    47   ans[0]:=tot;
    48 //  writeln(' ',tot);
    49   tot:=tot1;
    50 //  writeln('!!!',tot1);
    51   for i:=1 to 9 do
    52   mahoshojo(r,i,w2);
    53   ans[0]:=tot-ans[0];
    54   for i:=0 to 8 do write(ans[i],' ');
    55   writeln(ans[9])
    56 end.
    View Code

    话说记得类似的还有一道GDOI的题。。。。要求完全相反= =。。。给你1~某个数字中出现的各个数码的次数,要求判断是否存在这个数。。。

    //当然强省数位DP都是T1某弱省就变成了T3(雾。。。或者是T4?。。那年5道题= =)了。。。。。

    反正蒟蒻目测只会二分乱搞TAT.。。。。反正怎么玩都不会TLE2333

     //四个月没碰键盘TAT。。。脑子还剩一点但是代码能力已经回档到两年前了>_<

    //话说考前复习的时候来填坑显然花样作大死。。。YCL:哪有你这样的学生啊!。。。。。。。。

    //作死成功。。。

    接下来是bzoj3209题目地址:

    http://www.lydsy.com:808/JudgeOnline/problem.php?id=3209

    这种傻叉题只有我这种蒟蒻第一眼才没看出来= =

    就是把1833的加改成了乘。。。。。

    预处理出i位数中有j个1的情况总数(其实就是组合数= =组合数好久没打差点不会T_T)。。。。

    然后就没什么可扯的了

     1 var
     2   c:array[0..50,0..50]of int64;
     3   i,j,k:longint;
     4   n,m,ans,bilibili,temp:int64;
     5   num:array[0..50]of int64;
     6 function qpow(a,b:int64):int64;
     7 var date:int64;
     8 begin
     9   date:=1;
    10   while b>0 do
    11   begin
    12     if b and 1=1 then date:=date*a mod bilibili;
    13     b:=b shr 1;
    14     if b>0 then a:=a*a mod bilibili
    15   end;
    16   exit(date)
    17 end;
    18 begin
    19   bilibili:=10000007;
    20   readln(n);
    21   m:=trunc(ln(n)/ln(2))+1;
    22   for i:=0 to m do c[i,0]:=1;
    23   for j:=1 to m do
    24   for i:=1 to m do
    25   c[i,j]:=c[i-1,j]+c[i-1,j-1];
    26   i:=0;
    27   while n>0 do
    28   begin
    29     inc(i);
    30     num[m-i+1]:=n and 1;
    31     n:=n shr 1
    32   end;
    33   ans:=1;
    34   for i:=1 to m do
    35     if num[i]=1 then
    36     begin
    37       for j:=1 to m-i do
    38       ans:=(ans*qpow(temp+j,c[m-i,j]))mod bilibili;
    39       ans:=ans*(temp+1) mod bilibili;
    40       inc(temp)
    41     end;
    42   writeln(ans)
    43 end.
    View CodeII
  • 相关阅读:
    module.exports 和 exports的区别
    nodejs概述和理解
    sass的继承,混合宏,占位符的用法总结
    项目发布方式
    扩展运算符和解构赋值的理解
    C3----几个常用的加载图标制作
    gulp和yarn打包工具二分钟包会教程(高阶)
    Java接口
    Tomcat8 连接池
    DAMA
  • 原文地址:https://www.cnblogs.com/czllgzmzl/p/4161504.html
Copyright © 2011-2022 走看看