P4124 [CQOI2016]手机号码
#include <iostream>
#include <bits/stdc++.h>
#define int long long
using namespace std;
int l,r;
int dp[12][10][4][2][2][2];//当前位数,位数数字,连续个数,是否有4,是否有8,是否有至少3位连续数字
int num[11];
int dfs(int x,int pre,int l,bool _4,bool _8,bool _3,int tp){
if(tp == 1){
if(!_3){
if(l < 2) return 0;//不够长
if(l == 2)return x >= pre;
}else{
if(_4) return x >= 8 ? x:x+1;//有4无8
if(_8) return x >= 4 ? x:x+1;//有8无4
return x + 1;
}
}
int ct = x/num[tp-1];
int sum = 0;
for(int i = 0;i < ct;++i){
for(int j = 1;j <= 3;++j){
sum += dp[tp][i][j][0][0][1];//无论各种限制都合法
if(_3) sum += dp[tp][i][j][0][0][0];//前面有连续段后面就不需要了
if(!_8) sum += dp[tp][i][j][1][0][1];//没有8后面就能取4
if(!_4) sum += dp[tp][i][j][0][1][1];//没有4后面就能取8
if(_3 && !_8) sum += dp[tp][i][j][1][0][0];//后面可以取4且不必有连续段
if(_3 && !_4) sum += dp[tp][i][j][0][1][0];//后面可以取8且不必有连续段
}
if(i == pre && !_3){//之前没有连续段这一位填和上一位相同的
for(int j = 1;j <= l;++j){
sum += dp[tp][i][3-j][0][0][0];//接起来不短于3个后面没有4也没有8
if(!_8) sum += dp[tp][i][3-j][1][0][0];//接起来不短于3个后面有4无8
if(!_4) sum += dp[tp][i][3-j][0][1][0];//接起来不短于3个后面有8无4
}
}
}
l = (ct==pre) ? min(l + 1, 3ll) : 1;
_4 |= (ct==4);
_8 |= (ct==8);
_3 |= (l >= 3);//更新限制状态
if(_4 && _8) return sum;//填当前位卡上界不满足限制
return sum + dfs(x - ct * num[tp-1], ct, l, _4, _8, _3, tp - 1);//卡上界向后统计
}
int slv(int x){
int ct = 11;
int sum = 0;
if(x < num[ct - 1])return 0;
int nw = x / num[ct - 1];
for(int i = 1;i < nw;++i){
for(int j = 1;j <= 3;++j){
sum += dp[ct][i][j][1][0][1];//有4无8连续段
sum += dp[ct][i][j][0][1][1];//有8无4连续段
sum += dp[ct][i][j][0][0][1];//无8无4连续段
}
}
sum += dfs(x - nw * num[ct - 1], nw,1, nw == 4, nw == 8, 0 ,ct - 1);
return sum;
}
signed main(){
num[0] = 1;
for(int i = 1;i <= 10;i++) num[i] = num[i-1] * 10;
for(int i = 0;i <= 9;i++)dp[1][i][1][i==4][i==8][0] = 1;
for(int i = 2;i <= 11;i++){//第i为
for(int j = 0;j <= 9;j++){//第i位填j
for(int k = 0;k <= 9;k++){//上一位填k
for(int l = 1;l <= 3;l++){//截至上一位有连续l个数
for(int _4 = (k == 4);_4 <= 1;++_4){//是否有4
for(int _8 = (k == 8);_8 <= 1;++_8){//是否有8
for(int _3 = (l == 3);_3 <= 1;++_3){//是否有长度至少为3的连续段
int c = (j==k) ? min(l + 1,3ll) : 1;
bool fl1 = (j == 4) | _4;
bool fl2 = (j == 8) | _8;
bool fl3 = (c >= 3) | _3;
dp[i][j][c][fl1][fl2][fl3] += dp[i - 1][k][l][_4][_8][_3];
}
}
}
}
}
}
}
cin >> l >> r;
cout << slv(r) - slv(l - 1) << endl;
//system("pause");
return 0;
}