zoukankan      html  css  js  c++  java
  • hdu 4389 x mod f(x) 数位DP

    题意

      求区间[a,b] 之间, x%f(x) = 0的数量, 其中f(x)表示x的数位和.

    解题思路:

      转换成 F(A) = { x | x%f(x) = 0,  1 <= x <= A } , 然后结果即为 F(B)-F(A-1)

      首先定义一个状态. dp( L, i, j, k ) 表示长度为L的数位和为i,其对j取模结果为k的方案数.

      在同模j的情况下,则在该数后面增加一个x(0,9), 则得到 dp( L+1, i+x, j, (k*10+x)%j ).

      那么转移方程就是:

        dp( L+1, i+x, j, (k*10+x)%j ) += dp( L, i, j, k )

      接着就是如何利用已经得到的 dp( L, i, j, k )计算出 F(A).

      假设 A长度为L, 则A可以这么表示:  A = a1,a2,...,ai,...,aL.   (a1为高位.)

      则我们要求 所有的 B = b1,b2,...,bi,...,bl,  其中 B < A, l <= L,的所有满足 B%f(B) == 0的方案数.

      按位枚举, A的每一位取值,得到B, 例如,已经处理到了第i位时, 分三种情况:

       一. b1,..,bi-1 小于或等于 a1,...,ai-1 时:  则当前位置(i) bi 的取值为 [0,ai].

       二. b1,...,bi-1 大于 a1,...,ai-1 时:   由条件一就保证其不会出现.   

      其实这里有两个技巧的地方.

        1. 当 b1,...,bi-1 小于 a1,...,ai-1时, 那么我们后面的 bi,..,bL,就可以随意取了. 因为我们已经求得了 dp( L, i, j, k ) ,

    就可以记忆化得出结果.

        2. 因为我们让每一位的取值都从0 开始, 实际上我们这样就计算了长度小于L的.

      具体处理细节看代码:

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<string>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int N = 100;
    
    LL dp[10][N][N];
    int Mod;
    int a[N], b[N], A[10];
    LL res[N];
    
    LL dfs( bool less, int length, int sum, int mod ){
        if( length == 0 )
            return less && (sum==Mod) && (mod==0);
        if( less && (dp[length][sum][mod]!=-1) ) return dp[length][sum][mod];
        LL tmp = 0;    
        for(int x = 0; x < 10; x++){
            if( !less && (x>A[length-1]) ) break;    
            tmp += dfs( less || (x<A[length-1]), length-1, sum+x, (mod*10+x)%Mod );    
        }
        if( less ) dp[length][sum][mod] = tmp;
        return tmp;
    }
    LL solve( int x ){//x本身不被计算
        int tmp = x, L = 0;
        while( tmp ) A[L++] = tmp%10, tmp /= 10;
        //reverse( A, A+L );
        LL tot = 0;
        return dfs( false, L, 0, 0 );
    }
    int main(){
        int t;
        scanf("%d", &t);
        for(int i = 0; i < t; i++)
            scanf("%d%d", &a[i], &b[i] );
        memset( res, 0, sizeof(res));
        for(Mod = 1; Mod < N; Mod++){
            memset( dp, -1, sizeof(dp));
            for(int i = 0; i < t; i++ )
                res[i] += solve( b[i]+1 ) - solve( a[i] );
        }
        for(int i = 0; i < t; i++)
            printf("Case %d: %lld\n", i+1, res[i] );
        return 0;
    }

     

  • 相关阅读:
    从程序员到项目经理(16):原来一切问题都是可以解决的【转载】
    从程序员到项目经理(15):项目管理三大目标【转载】
    从程序员到项目经理(14):项目经理必须懂一点“章法”【转载】
    从程序员到项目经理(13):如何管理自己的时间(下)【转载】
    从程序员到项目经理(12):如何管理自己的时间(上)【转载】
    Linux的五个查找命令
    AWK 简明教程
    libstdc++.so.5: cannot open shared object file: No such file or directory
    中文分词器ICTCLAS使用方法(Java)
    Jetty实战之 安装 运行 部署
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3051005.html
Copyright © 2011-2022 走看看