[CQOI2016] 手机号码
[CQOI2016] 手机号码
题目大意:求\([L,R]\)之间11位的数字个数,要符合要有三连数字,且\(8\)和\(4\)不能同时出现
Solution
看代码吧
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#define sc(x) scanf("%lld", &x)
typedef long long ll;
ll dp[11][11][11][2][2][2];//dp[位置][上一个的上一个][上一个][是否有4][是否有8][是否三连]
int a[105];
ll dfs(int pos, int llast, int last, bool if4, bool if8, bool if3, bool dflag, bool uflag){
if(pos == -1) return if3 ? 1 : 0;//如果出现过连续三个相同的数,才符合条件。
if(!uflag && !dflag && last != -1 && llast != -1 && dp[pos][llast][last][if4][if8][if3] != -1)
return dp[pos][llast][last][if4][if8][if3];//不卡上下界才是一个完整的qwq
int up = uflag ? a[pos] : 9;
int down = dflag ? 1 : 0;
ll tmp = 0;
for(int i = down; i <= up; ++i){
if(if4 && i == 8) continue;
if(if8 && i == 4) continue;
tmp += dfs(pos - 1, last, i, if4 || (i == 4), if8 || (i == 8), if3 || (llast == last && last == i), 0, uflag && (i == a[pos]));
}
if(!uflag && !dflag && last != -1 && llast != -1) dp[pos][llast][last][if4][if8][if3] = tmp;
return tmp;
}
inline ll cal(ll x){
int cnt = -1;
memset(a, 0, sizeof(a));
while(x){
a[++cnt] = x % 10;
x /= 10;
}
return cnt != 10 ? 0: dfs(cnt, -1, -1, 0, 0, 0, 1, 1);//10位 例如1000000000, 就不存在
}
int main(){
ll l, r;
sc(l), sc(r);
memset(dp, -1, sizeof(dp));
printf("%lld\n", cal(r) - cal(l - 1));
return 0;
}