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;
}

 

 

posted @ 2013-04-29 19:10  yefeng1627  阅读(323)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor