HDU 4389 X mod f(x) (数位dp)

  终于把这题搞出来了。。。前后看了半一星期。比赛堆积的题目在减少。。。加油!

  数位dp,f[i][sum][mod][res]表示前i位,和为sum,模为mod,sum%mod结果为res的状态个数。f[i+1][sum+k][mod][(res*10 + k)%mod] += f[i][sum][mod][res];

予处理出所有的f[i][sum][mod][res], 然后从高到低逐位统计。比如当前位为a_i,则枚举当前位为0...(a_i-1) 这些数,i位往后的数的所有状态已经预处理出来了。。。所以出现 %mod == 0的情况直接累加就可以。。。最后单独处理一下最低位。。。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <vector>
  5 #include <cstring>
  6 #include <algorithm>
  7 #include <string>
  8 #include <set>
  9 #include <ctime>
 10 #include <queue>
 11 #include <map>
 12 #include <functional>
 13 #include <numeric>
 14 #include <sstream>
 15 
 16 #define CL(arr, val)    memset(arr, val, sizeof(arr))
 17 #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
 18 #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
 19 #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
 20 #define L(x)    (x) << 1
 21 #define R(x)    (x) << 1 | 1
 22 #define MID(l, r)   (l + r) >> 1
 23 #define Min(x, y)   x < y ? x : y
 24 #define Max(x, y)   x < y ? y : x
 25 #define E(x)    (1 << (x))
 26 
 27 const int eps = 1e-4;
 28 typedef long long LL;
 29 const int inf = ~0u>>2;
 30 using namespace std;
 31 
 32 const int N = 83;
 33 
 34 int f[12][N][N][N];
 35 int ten[15];
 36 
 37 
 38 void init() {
 39     int i, sum, mod, res, k, j;
 40     ten[0] = 1;
 41     for(i = 1; i <= 10; ++i)    ten[i] = ten[i-1]*10;
 42     CL(f, 0);
 43     for(i = 0; i < 10; ++i) {
 44         for(j = 1; j < N; ++j)  f[1][i][j][i%j] = 1;
 45     }
 46 
 47     for(i = 1; i < 9; ++i) {
 48         for(sum = 0; sum < N; ++sum) {
 49             for(mod = 1; mod < N; ++mod) {
 50                 for(res = 0; res < N; ++res) {
 51                     if(!f[i][sum][mod][res])    continue;
 52                     for(k = 0; k < 10 && sum + k < N; ++k)
 53                         f[i+1][sum + k][mod][(res*10 + k)%mod] += f[i][sum][mod][res];
 54                 }
 55             }
 56         }
 57     }
 58 }
 59 
 60 int a[15];
 61 
 62 int Do(int x) {
 63     if(x <= 10) return x;
 64     int tsum = 0, i, j, n = 0, u;
 65     int mod, sum, num, res, ans = 0;
 66 
 67     for(u = x ; u > 0; u /= 10)  a[++n] = u%10, tsum += u%10;
 68 
 69     for(mod = 1; mod <= 9*n; ++mod) {    //枚举取模,也就是所有数位的和
 70         if(mod > x) break;
 71         sum = mod;
 72         num = 0;
 73         for(i = n; i > 1; --i) {
 74             for(j = 0; j < a[i] && j <= sum; ++j) {    //从0...(a_i - 1)枚举。第i位的数
 75                 for(res = 0; res < mod; ++res) {    //res 表示i位以后的数位和取模得到的值
 76                     if((num + j*ten[i-1] + res)%mod == 0) {    //根据同余公式
 77                         ans += f[i-1][sum - j][mod][res];
 78                     }
 79                 }
 80             }
 81             sum -= a[i];
 82             num += a[i]*ten[i-1];
 83         }
 84     }
 85     while(1) {    //单独处理最低位
 86         if(x%tsum == 0) ans++;
 87         if(x%10 == 0)   break;
 88         x -- ; tsum --;
 89     }
 90     return ans;
 91 }
 92 
 93 int main() {
 94     freopen("data.in", "r", stdin);
 95 
 96     init();
 97     int t, a, b, cas = 0;
 98     scanf("%d", &t);
 99     while(t--) {
100         scanf("%d%d", &a, &b);
101         printf("Case %d: %d\n", ++cas, Do(b) - Do(a - 1));
102     }
103     return 0;
104 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值