TopCoder SRM 363 Div1 Meduim MirrorNumber

問題

ある数字がmirror numberであるとは、
その数字を鏡に映しても意味のある数字になっていることをいう。
数字を鏡に映すと、1は1のまま2は5に、5は2に、8は8に、0は0になり、
数字の順序が逆になる。
反転前または反転後にleading zeroがあることは許されない。


A以上B以下の数のうち、mirror numberである数の個数を求めよ。

制約条件

A,B≦10^18

方針

動的計画法による。
n以下のmirror numberの数を高速に数えられればよい。
m桁ちょうどのmirror numberの数を数えることを考える。

dp[i][s][t]を、
i桁目まで数字を頭から決めたとき、
s=0なら数字がnよりも小さくなっておらず、s=1なら数字がnよりも小さくなっている
t=0なら数字の末尾の部分がnよりも小さく、
t=1なら数字の末尾の部分がちょうどnの末尾に一致し、
t=2なら数字の末尾の部分がnよりも大きくなっているような状態の場合の数とする。


これを更新していくDPをすればよい。
半分の桁を決め終えた後、s=1であるか、t<2であれば、それはn以下のmirror numberとなっているので、答えにその数を足す。

ソースコード

int mirror(int n){
  if(n==2||n==5)return 7-n;
  return n;
}
int m,d[20],dp[20][2][3];
ll calc2(int m){
  memset(dp,0,sizeof(dp));
  dp[0][0][1]=1;
  ll res=0;
  rep(i,(m+1)/2)rep(s,2)rep(t,3){
    rep(j,10)if(j==0||j==1||j==2||j==5||j==8){
      int ns=s||j<d[i], nt=mirror(j)!=d[m-i-1]?mirror(j)<d[m-i-1]?0:2:t;
      if(!ns&&j>d[i])continue;
      if(i==0&&j==0||i*2+1==m&&(j==2||j==5))continue;
      if((i+1)*2<m)dp[i+1][ns][nt]+=dp[i][s][t];
      else if(ns||nt<2)res+=dp[i][s][t];
    }
  }
  return res;
}
ll calc(ll n){
  if(n<0)return 0;
  for(m=0;n;n/=10)d[m++]=n%10;
  reverse(d,d+m);
  
  ll res=1;
  for(int i=m;i>0;i--){
    res+=calc2(i);
    rep(j,m)d[j]=10;
  }
  return res;
}
class MirrorNumber {
  public:
  int count(string A, string B) {
    return calc(atoll(B.c_str()))-calc(atoll(A.c_str())-1);
  }
};