• 【数位dp】bzoj1799: [Ahoi2009]self 同类分布


    各种奇怪姿势的数位dp

    Description

    给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。

    Sample Input

    10 19

    Sample Output

    3

    HINT

    【约束条件】1 ≤ a ≤ b ≤ 10^18


    题目分析

    好像10^18左右的数位dp都是乱搞就好了

    既然是要求整除原数,那么数位之和肯定要放进状态里,并且枚举数位总和地去dp。

    看上去好像$f[i][j]$表示后面$i$位总和为$j$的合法方案数不就好了吗?

    考虑这种状态的转移会发现,从后面做上来不可行啊,没办法处理在开头加上一个数后,能整除数位之和的方案。并且我们启发性地发现,为了转移的合法性,需要在状态里加上余数来限制状态(因为当前不能整除的或许后面能够整除了;反之亦有可能)。

    于是想到用$f[i][j][k][done(0/1)]$表示前$i$位和为$j$,除数位总和$sum$的余数为$k$,是否达到上限(done=1表示达到)的合法状态数。

    最终答案显然是$sum{f[digits][sum][0][0]+f[digits][sum][0][1]}$。当然这个sum是要枚举过去的。

    如此,转移也就不难处理了,并且时间复杂度也是正确的(位数最大就18)

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 
     4 ll a,b;
     5 ll f[23][203][203][2];
     6 int digit[23];
     7 
     8 ll read()
     9 {
    10     char ch = getchar();
    11     ll num = 0;
    12     bool fl = 0;
    13     for (; !isdigit(ch); ch = getchar())
    14         if (ch=='-') fl = 1;
    15     for (; isdigit(ch); ch = getchar())
    16         num = (num<<1)+(num<<3)+ch-48;
    17     if (fl) num = -num;
    18     return num;
    19 }
    20 ll solve(ll x)
    21 {
    22     for (digit[0]=0; x; x/=10)
    23         digit[++digit[0]] = x%10;
    24     for (int i=1; i<=digit[0]/2; i++)
    25         std::swap(digit[i], digit[digit[0]-i+1]);
    26     int mx = digit[0]*9;
    27     ll ret = 0;
    28     for (int sum=1; sum<=mx; sum++)
    29     {
    30         memset(f, 0, sizeof f);
    31         f[0][0][0][1] = 1;
    32         for (int i=0; i<digit[0]; i++)
    33             for (int j=0; j<=i*9; j++)
    34                 for (int k=0; k<sum; k++)
    35                     for (int p=0; p<=1; p++)
    36                         if (f[i][j][k][p])
    37                             for (int t=0; t<=9; t++){
    38                                 if (p&&digit[i+1] < t) break;
    39                                 f[i+1][j+t][(10*k+t)%sum][p&&digit[i+1]==t] += f[i][j][k][p];
    40                             }
    41         ret += f[digit[0]][sum][0][0]+f[digit[0]][sum][0][1];
    42     }
    43     return ret;
    44 }
    45 int main()
    46 {
    47     a = read(), b = read();
    48     printf("%lld
    ",solve(b)-solve(a-1));
    49     return 0;
    50 }

    END

  • 相关阅读:
    java——异常(一)
    java —— 全面解析 Annotation
    多线程(一)——三种实现方式
    java ——static 关键词总结
    java —— equals 与 ==
    java——数组与内存控制
    java—— finall 关键词
    File类实现文件夹和文件复制
    java —— 内部类
    类成员——代码块
  • 原文地址:https://www.cnblogs.com/antiquality/p/9353668.html
Copyright © 2020-2023  润新知