競プロ覚書

思考整理

ABC007 D - 禁止された数字

atcoder.jp
桁DPの典型的な問題

【題意】
自然数区間[A, B]において、4または9を含む整数はいくつあるか?
【典型ポイント】
1~Nの数え上げは桁DP
区間
【方針】
dp[i][smaller][j]:=1~Nの整数のうち、前方i桁まで見て、4または9が含まれている(j==1)か含まれていない(j==0)場合の数
smaller:未満フラグ
・遷移( dp[i+1][smaller | x

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<long long, long long>;
constexpr char ln =  '\n';
constexpr long long MOD = 1000000007;
constexpr long long INF = 1000000000 + 100;
constexpr long long LINF = 1000000000000000000 + 100;
#define all(v) v.begin(), v.end()
#define rep(i, n) for(int i=0;i<(n);i++)
#define rept(i, j, n) for(int i=(j); i<(n); i++)
#define rrep(i, n) for(int i=(n); i>=0; i--)
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }


int main(){
    ll A; string B, C; cin >> A >> B;
    C = to_string(A-1);

    auto digit_dp = [&](string N) -> ll{
        const int n = N.size();
        ll dp[n+1][2][2]{};
        dp[0][0][0] = 1;
        for(int i=0; i<n; i++){
            for(int smaller=0; smaller<2; smaller++){
                for(int j=0; j<2; j++){
                    for(int x=0; x<=(smaller ? 9 : N[i]-'0'); x++){
                        dp[i+1][smaller | x<N[i]-'0'][j or x==4 or x==9] += dp[i][smaller][j];
                    }
                }
            }
        }
        return dp[n][0][1] + dp[n][1][1];
    };
    cout << digit_dp(B) - digit_dp(C) << ln;
}