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;

}

 

 

你可能感兴趣的:(HDU)