【题目描述】

人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不 吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数 量。

工具需要检测的号码特征有两个:

  • 号码中要出现至少3个相邻的相同数字,
  • 号码中不能同时出现8和4。

号码必须同时包含两个特征才满足条件。

满足条件的号码例如:13000988721、23333333333、14444101000。 而不满足条件的号码例如:1015400080、10010012022。 手机号码一定是11位数,前不含前导的0。 工具接收两个数L和R,自动统计出[L,R]区间内所有满足条件的号码数量。 L和R也是11位的手机号码。

【题目链接】

BZOJ 4521 手机号码 【CQOI 2016】

【解题思路】

裸数位DP....就是状态复杂了点。

$ f(i, j, w, a, d4, d8) $表示共$ j $位,首位为$ j $且开头有$ w $个$ j $,$ a $表示是否有三个相邻的相同数字,$d4, d8$分别表示是否含有数字4、8。

转移嘛......自己YY去~除了麻烦点儿倒也不难想......

统计答案就是平常数位DP的套路咯,还要记录一下是否出现4、8或连续三位数字。

写完真是浑身舒爽...

【AC代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
 
typedef long long int64;
 
const int MAXD = 11;
 
int64 f[MAXD + 1][10][3 + 1][2][2][2];
 
inline void init(){
    memset(f, 0, sizeof f);
    for(int i = 0; i < 10; i++) f[1][i][1][0][i == 4][i == 8] = 1;
 
    for(int i = 2; i <= MAXD; i++){
        for(int j = 0; j <= 9; j++){
            for(int k = 0; k <= 9; k++){
                for(int f4 = 0; f4 <= 1; f4++){
                    for(int f8 = 0; f8 <= 1; f8++){
                        int d4 = f4 || j == 4, d8 = f8 || j == 8;
                        if(j != k){
                            for(int w = 1; w <= 3; w++){
                                for(int z = 0; z <= 1; z++){
                                    f[i][j][1][z][d4][d8] += f[i - 1][k][w][z][f4][f8];
                                }
                            }
                        } else{
                            f[i][j][2][0][d4][d8] += f[i - 1][k][1][0][f4][f8];
                            f[i][j][2][1][d4][d8] += f[i - 1][k][1][1][f4][f8];
 
                            f[i][j][3][1][d4][d8] += f[i - 1][k][2][0][f4][f8];
                            f[i][j][3][1][d4][d8] += f[i - 1][k][2][1][f4][f8];
                            f[i][j][3][1][d4][d8] += f[i - 1][k][3][1][f4][f8];
                        }
                    }
                }
            }
        }
    }
}

inline int split(int64 x, int d[]){
    int n = 0;
    while(x) d[n++] = x % 10, x /= 10;
    for(int i = 0; i < n >> 1; i++) std::swap(d[i], d[n - i - 1]);
    return n;
}
 
inline int64 calc(int64 x){
    int d[MAXD];
    int n = split(x, d);
    int64 ans = 0;
 
    for(int i = 1; i < n; i++){
        for(int j = 1; j <= 9; j++){
            for(int w = 1; w <= 3; w++){
                ans += f[i][j][w][1][0][0];
                ans += f[i][j][w][1][1][0];
                ans += f[i][j][w][1][0][1];
            }
        }
    }
 
    for(int i = 1; i < d[0]; i++){
        for(int w = 1; w <= 3; w++){
            ans += f[n][i][w][1][0][0];
            ans += f[n][i][w][1][1][0];
            ans += f[n][i][w][1][0][1];
        }
    }
 
    int pre1 = d[0], pre2 = -1;
    int d4 = d[0] == 4, d8 = d[0] == 8, f3 = 0;
 
    for(int i = 1; i < n; i++){
        int x = n - i;
        for(int j = 0; j < d[i]; j++){
            for(int w = 1; w <= 3; w++){
                ans += f[x][j][w][1][0][0];
                if(!d4) ans += f[x][j][w][1][0][1];
                if(!d8) ans += f[x][j][w][1][1][0];
 
                if(f3){
                    ans += f[x][j][w][0][0][0];
                    if(!d4) ans += f[x][j][w][0][0][1];
                    if(!d8) ans += f[x][j][w][0][1][0];
                }
            } // 连续三位全部在 未确定部分 或全在 确定部分 的情况
 
            if(f3) continue;
 
            if(pre1 == j){
                ans += f[x][j][2][0][0][0];
                if(!d4) ans += f[x][j][2][0][0][1];
                if(!d8) ans += f[x][j][2][0][1][0];
 
                if(pre1 == pre2){
                    ans += f[x][j][1][0][0][0];
                    if(!d4) ans += f[x][j][1][0][0][1];
                    if(!d8) ans += f[x][j][1][0][1][0];
                }
            } // 连续三位在确定与未确定交界处的情况
        }
 
        if(pre1 == pre2 && pre1 == d[i]) f3 = 1;
 
        pre2 = pre1, pre1 = d[i];
 
        if(pre1 == 4){
            if(d8) break;
            d4 = 1;
        }
 
        if(pre1 == 8){
            if(d4) break;
            d8 = 1;
        }
    }
 
    return ans;
}
 
inline int64 solve(int64 a, int64 b){
    return calc(b + 1) - calc(a);
}
 
int main(){
    int64 a, b;
    init();
 
    scanf("%lld%lld", &a, &b);
 
    printf("%lld\n", solve(a, b));
 
    return 0;
}

就是这样咯