• bzoj2757


    非常神的数位dp,我调了几乎一天
    首先和bzoj3131类似,乘积是可以预处理出来的,注意这里乘积有一个表示的技巧
    因为这里质因数只有2,3,5,7,所以我们可以表示成2^a*3^b*5^c*7^d,也就是v[a,b,c,d]这个数组
    这样也非常方便进行除法,乘法的状态转移,转移和要维护的东西比较多,我就直接在程序里注释了
    这里维护转移的核心思想是算每一位对编号和的贡献,乘积为0和非0分开计算

      1 const mo=20120427;
      2       lim=1000000000000000000;
      3       q:array[1..9,1..4] of longint=((0,0,0,0),(1,0,0,0),(0,1,0,0),(2,0,0,0),(0,0,1,0),
      4                                      (1,1,0,0),(0,0,0,1),(3,0,0,0),(0,2,0,0));
      5 //1~9对乘积的影响
      6 var v:array[0..60,0..38,0..30,0..22] of longint;
      7     f,s:array[0..20,0..70010] of longint;
      8     p,r,g,h:array[0..20] of longint;
      9     pp:array[0..20] of int64;
     10     n,a,b,c,d,i,j,t,m,k:longint;
     11     x,y,w:int64;
     12 
     13 procedure up(var a:longint; b:longint);
     14   begin
     15     a:=(a+b+mo) mod mo;
     16   end;
     17 
     18 procedure pre;
     19   var i,j,k,p:int64;
     20       a,b,c,d:longint;
     21   begin
     22     i:=1;
     23     a:=0;
     24     while i<=lim do
     25     begin
     26       j:=i; b:=0;
     27       while j<=lim do
     28       begin
     29         k:=j; c:=0;
     30         while k<=lim do
     31         begin
     32           p:=k; d:=0;
     33           while p<=lim do
     34           begin
     35             inc(t);
     36             v[a,b,c,d]:=t;
     37             p:=p*7;
     38             inc(d);
     39           end;
     40           k:=k*5;
     41           inc(c);
     42         end;
     43         j:=j*3;
     44         inc(b);
     45       end;
     46       i:=i*2;
     47       inc(a);
     48     end;
     49   end;
     50 
     51 procedure work;
     52   var a,b,c,d,i,j:longint;
     53   begin
     54     f[0,1]:=1;
     55     for i:=1 to 18 do
     56       for a:=0 to 60 do for b:=0 to 38 do for c:=0 to 26 do for d:=0 to 22 do
     57         if (v[a,b,c,d]>0) and (f[i-1,v[a,b,c,d]]>0) then
     58         begin
     59           for j:=1 to 9 do
     60           begin
     61             up(f[i,v[a+q[j,1],b+q[j,2],c+q[j,3],d+q[j,4]]],f[i-1,v[a,b,c,d]]);
     62 //f[i,S]表示i位数乘积为S的数的个数
     63             up(s[i,v[a+q[j,1],b+q[j,2],c+q[j,3],d+q[j,4]]],s[i-1,v[a,b,c,d]]+int64(f[i-1,v[a,b,c,d]])*int64(j)*int64(p[i]) mod mo);
     64 //s[i,S]表示i位数乘积为S的数的和,转移就是算当前第i位对和的贡献,显然是j(第i位的数)*p[i](位权)*f[i-1,S/j]
     65           end;
     66         end
     67         else if v[a,b,c,d]=0 then break;
     68     h[0]:=1;
     69     for i:=1 to 18 do
     70     begin
     71       h[i]:=h[i-1]*9 mod mo;  //i位数不含0的个数
     72       g[i]:=(g[i-1]*9+45*int64(p[i])*int64(h[i-1]) mod mo) mod mo;  //i位数不含0的数的和,45=1+..+9
     73     end;
     74   end;
     75 
     76 procedure get(x:int64);
     77   begin
     78     m:=0;
     79     while x>0 do
     80     begin
     81       inc(m);
     82       r[m]:=x mod 10;
     83       x:=x div 10;
     84     end;
     85   end;
     86 
     87 function can(x:int64):boolean;
     88   begin
     89     a:=0; b:=0; c:=0; d:=0;  //拆分成2^a*3^b*5^c*7^d
     90     while x mod 2=0 do
     91     begin
     92       inc(a);
     93       x:=x div 2;
     94     end;
     95     while x mod 3=0 do
     96     begin
     97       inc(b);
     98       x:=x div 3;
     99     end;
    100     while x mod 5=0 do
    101     begin
    102       inc(c);
    103       x:=x div 5;
    104     end;
    105     while x mod 7=0 do
    106     begin
    107       inc(d);
    108       x:=x div 7;
    109     end;
    110     if x=1 then exit(true) else exit(false);
    111   end;
    112 
    113 function sum(a:int64):longint;  //防止爆int64的计算
    114   var b:int64;
    115   begin
    116     b:=a-1;
    117     if a mod 2=0 then a:=a div 2
    118     else b:=b div 2;
    119     a:=a mod mo;
    120     b:=b mod mo;
    121     exit(a*b mod mo);
    122   end;
    123 
    124 function count(x,k:int64):longint;
    125   var i,j,pre,a1,b1,c1,d1,ans:longint;
    126       now:int64;
    127   begin
    128     if not can(k) then exit(0);  //k不存在
    129     get(x);
    130     ans:=0;
    131     for i:=1 to m-1 do  //统计1~m-1位数乘积为k数的和
    132       up(ans,s[i,v[a,b,c,d]]);
    133     now:=1;
    134     pre:=0;  //pre=∑(r[i]*p[i])
    135     for i:=m downto 1 do
    136     begin
    137       now:=now*int64(r[i]); 
    138       if r[i]=0 then break; //显然
    139       for j:=1 to r[i]-1 do
    140       begin
    141         a1:=a-q[j,1]; b1:=b-q[j,2]; c1:=c-q[j,3]; d1:=d-q[j,4];
    142         if (a1<0) or (b1<0) or (c1<0) or (d1<0) then continue;
    143         up(ans,s[i-1,v[a1,b1,c1,d1]]+int64(pre+j*p[i])*int64(f[i-1,v[a1,b1,c1,d1]]) mod mo);
    144 //和与处理的转移类似
    145       end;
    146       a:=a-q[r[i],1]; b:=b-q[r[i],2]; c:=c-q[r[i],3]; d:=d-q[r[i],4];
    147       up(pre,r[i]*p[i] mod mo);
    148       if (a<0) or (b<0) or (c<0) or (d<0) then break;
    149     end;
    150     if now=k then up(ans,x mod mo);  //判断x
    151     exit(ans);
    152   end;
    153 
    154 function calc(x:int64):longint;  //乘积为0的转移要复杂,容易出错
    155   var i,j,pre,ans,e:longint;
    156   begin
    157     get(x);
    158     ans:=sum(p[m]) mod mo;  //先求1~m-1乘积为0的数和,我们可以用补集的思想,总和-不含0的数和
    159     for i:=1 to m-1 do
    160       up(ans,-g[i]);
    161     pre:=0;
    162     for i:=m downto 1 do
    163     begin
    164       if r[i]=0 then
    165       begin
    166         up(ans,int64(pre)*int64(x mod pp[i] mod mo+1) mod mo+sum(x mod pp[i]+1));
    167 //当前为0,后面对答案的贡献可以直接算出来
    168         break;
    169       end;
    170       if i<m then up(ans,int64(pre)*int64(p[i]) mod mo+sum(pp[i]));
    171 //否则m位数且小于等于x的数当前位可以是0,计算贡献
    172       if i=1 then e:=r[i] else e:=r[i]-1;
    173       for j:=1 to e do
    174         up(ans,sum(pp[i])-g[i-1]+int64(pre+j*p[i])*int64((p[i]-h[i-1]+mo) mod mo) mod mo);
    175 //这里还是运用到补集的思想,和之前算乘积不为0类似
    176       up(pre,r[i]*p[i] mod mo);
    177     end;
    178     exit(ans);
    179   end;
    180 
    181 begin
    182   readln(n);
    183   p[1]:=1;
    184   pp[1]:=1;
    185   for i:=2 to 18 do
    186   begin
    187     p[i]:=p[i-1]*10 mod mo;  //位权
    188     pp[i]:=pp[i-1]*10;
    189   end;
    190   pre;
    191   work;
    192   for i:=1 to n do
    193   begin
    194     readln(x,y,w);
    195     if w=0 then writeln((calc(y)-calc(x-1)+mo) mod mo)
    196     else writeln((count(y,w)-count(x-1,w)+mo) mod mo);
    197   end;
    198 end.
    View Code
  • 相关阅读:
    ubuntu中source insight打不开,报错pagefault的解决方法
    第六次团队作业——Alpha冲刺之事后诸葛亮
    Alpha阶段总结
    第五次团队作业——第一次项目冲刺——Alpha版本
    第四次团队作业——系统设计
    团队项目——需求规格说明书
    第二次团队作业——预则立&&他山之石
    团队项目时间规划2016
    第二次结对编程作业——毕设导师智能匹配
    第二次团队作业——团队选题报告
  • 原文地址:https://www.cnblogs.com/phile/p/4472933.html
Copyright © 2020-2023  润新知