[CQOI2016]手机号码

嘟嘟嘟


这题一看就是数位dp。
我写数位dp,一般是按数位dp的格式写一个爆搜,然后加一点记忆化。
不过其实我一直不是很清楚记忆化是怎么加,感觉就是把dfs里的参数都扔到dp数组里,好像很暴力啊。
这题有一个坑点就是数字必须是电话号码,也就是11位且没有前导零。因此关于前导零的处理是最高位不能为0,剩下的都可以。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
//const int maxn = ;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int num[15], cnt = 0;
ll dp[15][10][10][2][2][2][2];
In ll dfs(int now, int pre1, int pre2, bool _lian, bool _4, bool _8, bool _zero, bool _Max)
{
  if(_4 && _8) return 0;
  if(!now) return _lian;
  if(~pre1 && ~pre2 && ~dp[now][pre1][pre2][_lian][_4][_8][_Max]) return dp[now][pre1][pre2][_lian][_4][_8][_Max];
  int Min = _zero ? 1 : 0, Max = _Max ? num[now] : 9;
  ll ret = 0;
  for(int i = Min; i <= Max; ++i)
    ret += dfs(now - 1, i, pre1, _lian || (i == pre1 && i == pre2), _4 || (i == 4), _8 || (i == 8), _zero && !i, _Max && i == Max);
  if(~pre1 && ~pre2) dp[now][pre1][pre2][_lian][_4][_8][_Max] = ret;
  return ret;
}
In ll solve(ll n)
{
  cnt = 0;
  while(n) num[++cnt] = n % 10, n /= 10;
  if(cnt < 11) return 0;
  Mem(dp, -1);
  return dfs(cnt, -1, -1, 0, 0, 0, 1, 1);
}

int main()
{
  ll l = read(), r = read();
  write(solve(r) - solve(l - 1)), enter;
  return 0;
}
posted @ 2019-03-02 13:34  mrclr  阅读(164)  评论(0编辑  收藏  举报