hdu 5898 - odd-even number (2016沈阳网络赛) - 数位dp

题意:如果一个数字连续的奇数都为偶数个,连续的偶数个数都为奇数,那么这个数字就是奇偶数,输入l,r,问[l, r]闭区间有多少个奇偶数。

数位dp用dp[i][j][k]表示长度为i并且最高位为j的数字并且与最高位数字奇偶情况相同的连续位数为k的满足题目条件的数字有多少个,k=0表示偶数长度,k=1表示奇数长度。状态转移的条件会比较麻烦:

如果高位为奇数位偶数,可以向偶数位偶数和奇数位奇数转移(就是可以在最高位前面添加以为奇数或者以为偶数);

如果高位为偶数位偶数,那么只能在前面添加一位偶数,即只能向奇数位偶数转移;

如果高位有偶数位奇数,可以向奇数位奇数转移也可以向奇数位偶数转移;

如果高位为奇数位奇数,只能向偶数位奇数转移。

查询和一般的数位dp差不太多,要注意高位的数字对当前状态的影响。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][10][2];
ll pw[20];
int judge(int l, int m, int j, int k) {
    if(!l && !m && !j && k) return 1;
    if(!l && m && !j && !k) return 1;
    if((l ^ m) && j && k) return 1;
    if(l && !m && !j && k) return 1;
    if(l && m && j && !k) return 1;
    return 0;
}
void init() {
    dp[0][1][0] = 1;
    int i, j, k, s, l, m;
    for(i = 1; i < 20; i++) {
        for(j = 0; j < 10; j++) {
            for(k = 0; k < 2; k++) {
                for(l = 0; l < 10; l++) {
                    for(m = 0; m < 2; m++) {
                        if(judge(l & 1, m, j & 1, k)) {
                            dp[i][j][k] += dp[i - 1][l][m];
                        }
                    }
                }
            }
        }
    }
    pw[0] = 1;
    for(i = 1; i < 19; i++) {
        pw[i] = pw[i - 1] * 10;
    }
}
ll add(ll u, int l, int o = 0, int n = 0, int f = 0) {
    if(!u) return 0;
    ll ans = 0;
    ll t = pw[l - 1] * (u / pw[l - 1]);
    for(int i = (f ? l : 1); i <= l; i++) {
        for(int j = (f ? 0 : 1); j < 10 && j * pw[i - 1] < t; j++) {

            if(!f || (o && !n)) ans += dp[i][j][(j & 1) ^ 1];
            else if(o == 0 && n == 1) {
                ans += dp[i][j][0];
            }
            else if(o && n && (j & 1)) {
                ans += dp[i][j][1];
            }
            else if(!o && !n && (j & 1) == 0){
                ans += dp[i][j][1];
            }
        }

    }
    int x = (u / pw[l - 1]) & 1;
    if(x == o) ans += add(u % pw[l - 1], l - 1, x, n ^ 1,f + 1);
    else if((o ^ n) || !f) ans += add(u % pw[l - 1], l - 1, x, 1,f + 1);
    return ans;
}
int main() {
    init();
    ll d;
    int t, ks = 1;
    ll l, r;
    scanf("%d", &t);
    while(t--) {
        scanf("%I64d%I64d", &l, &r);
        int lt1 = 0, lt2;
        r++;
        for(lt1 = 18; pw[lt1] > l; lt1--);
        for(lt2 = 18; pw[lt2] > r; lt2--);
        printf("Case #%d: %I64d\n", ks++, add(r, lt2 + 1) - add(l, lt1 + 1));
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值