結果

問題 No.260 世界のなんとか3
ユーザー qwewe
提出日時 2025-05-14 13:01:24
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 340 ms / 2,000 ms
コード長 8,306 bytes
コンパイル時間 848 ms
コンパイル使用メモリ 73,996 KB
実行使用メモリ 19,864 KB
最終ジャッジ日時 2025-05-14 13:03:34
合計ジャッジ時間 7,023 ms
ジャッジサーバーID
(参考情報)
judge1 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 27
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <string>
#include <vector>
#include <cstring> // For memset

using namespace std;

// Define MOD constant, 10^9 + 7
const long long MOD = 1e9 + 7;

// Global variables used by the DP function
string N_str; // Current upper bound N as a string
char target_type; // Target set identifier: 'A', 'B', or 'I' (for Intersection)

// Memoization table for DP states
// State dimensions: idx, tight, leading_zeros, rem3, rem8, has3
// Maximum length of N is 10000, so idx can go up to 10000. Size 10001 needed.
// Other dimensions are small: 2, 2, 3, 8, 2 respectively.
long long memo[10001][2][2][3][8][2];

// Digit DP function
// Counts numbers x in [1, N] matching properties defined by target_type
// idx: current digit index being processed (from left, 0-based)
// tight: boolean, true if we are restricted by the digits of N, false otherwise
// leading_zeros: boolean, true if we are currently placing leading zeros
// rem3: current number modulo 3
// rem8: current number modulo 8
// has3: boolean, true if the number contains the digit 3
long long dp(int idx, bool tight, bool leading_zeros, int rem3, int rem8, bool has3) {
    // Base case: Reached the end of the number string
    if (idx == N_str.length()) {
        // If the number formed was 0 (all digits were leading zeros), it's not in [1, N].
        if (leading_zeros) return 0; 

        // Check if the formed number satisfies the conditions for the target set
        if (target_type == 'A') {
            // Set A: x is NOT divisible by 3 AND does NOT contain digit 3
            return (rem3 != 0 && !has3);
        } else if (target_type == 'B') {
            // Set B: x IS divisible by 8
            return (rem8 == 0);
        } else if (target_type == 'I') { // 'I' for Intersection A intersect B
            // Set A intersect B: x is NOT divisible by 3 AND does NOT contain digit 3 AND IS divisible by 8
            return (rem3 != 0 && !has3 && rem8 == 0);
        }
        // Should not reach here if target_type is valid
        return 0; 
    }

    // Check memoization table to avoid recomputing states
    long long &mem_val = memo[idx][tight][leading_zeros][rem3][rem8][has3];
    if (mem_val != -1) {
        return mem_val;
    }

    long long count = 0; // Accumulator for the count, modulo MOD
    // Determine the upper limit for the current digit `d`
    int limit = tight ? (N_str[idx] - '0') : 9;

    // Iterate through possible digits `d` for the current position `idx`
    for (int d = 0; d <= limit; ++d) {
        // Update the 'tight' constraint for the next recursive call
        bool new_tight = tight && (d == limit);
        
        // Handle the logic based on whether we are currently placing leading zeros
        if (leading_zeros && d == 0) {
             // If still placing leading zeros (current digit is 0), continue with leading_zeros=true
             // The state for number 0 is (rem3=0, rem8=0, has3=false)
             count = (count + dp(idx + 1, new_tight, true, 0, 0, false)) % MOD;
        } else {
             // If d > 0 (first non-zero digit) or if we were already past leading zeros
             bool new_leading_zeros = false; // State transitions to not having leading zeros
             int new_rem3, new_rem8;
             bool new_has3;

             // Calculate the new state based on whether 'd' is the first non-zero digit
             if (leading_zeros) { // `d` is the first non-zero digit
                 new_rem3 = d % 3;
                 new_rem8 = d % 8;
                 new_has3 = (d == 3);
             } else { // `d` is a subsequent digit appended to a non-zero prefix
                 new_rem3 = (rem3 * 10 + d) % 3;
                 new_rem8 = (rem8 * 10 + d) % 8;
                 new_has3 = has3 || (d == 3); // Update has3 if d is 3 or if it was already true
             }
            
             // Make the recursive call for the next digit position
             count = (count + dp(idx + 1, new_tight, new_leading_zeros, new_rem3, new_rem8, new_has3)) % MOD;
        }
    }

    // Store the computed result in the memoization table and return it
    return mem_val = count;
}

// Function to calculate the count of numbers up to N (represented by num_str)
// satisfying the problem's condition: Aho AND NOT Seishun.
// Uses the principle of inclusion-exclusion: N - |A U B| = N - (|A| + |B| - |A intersect B|)
long long count_N(string num_str) {
    // Base case: If N is "0", the count of numbers in [1, 0] is 0.
    if (num_str == "0") {
        return 0;
    }

    N_str = num_str; // Set the global N string for the DP function

    // Calculate N modulo MOD. Needed for the final subtraction step.
    long long N_mod_MOD = 0;
    for (char c : N_str) {
        N_mod_MOD = (N_mod_MOD * 10 + (c - '0')) % MOD;
    }

    // Calculate count for set A: x % 3 != 0 AND not has3
    memset(memo, -1, sizeof(memo)); // Initialize memo table with -1
    target_type = 'A';
    long long count_A = dp(0, true, true, 0, 0, false); // Initial DP call starts at index 0
    
    // Calculate count for set B: x % 8 == 0
    memset(memo, -1, sizeof(memo)); // Reset memo table
    target_type = 'B';
    long long count_B = dp(0, true, true, 0, 0, false);

    // Calculate count for set A intersect B: x % 3 != 0 AND not has3 AND x % 8 == 0
    memset(memo, -1, sizeof(memo)); // Reset memo table
    target_type = 'I';
    long long count_A_intersect_B = dp(0, true, true, 0, 0, false);

    // Calculate |A union B| = |A| + |B| - |A intersect B| modulo MOD
    long long union_count = (count_A + count_B) % MOD;
    union_count = (union_count - count_A_intersect_B + MOD) % MOD; // Add MOD before taking modulo to handle potential negative result
    
    // The final count is N - |A union B| modulo MOD.
    // This counts numbers x where NOT (A or B) holds.
    // NOT A is (x % 3 == 0 OR has3) which is the "Aho" condition.
    // NOT B is (x % 8 != 0) which is the "NOT Seishun" condition.
    // So we count numbers that are Aho AND NOT Seishun.
    long long result = (N_mod_MOD - union_count + MOD) % MOD; // Add MOD before modulo for subtraction
    return result;
}

// Function to subtract 1 from a number represented as a string
string subtract_one(string S) {
    // Special case: If S is "1", subtracting 1 gives "0"
    if (S == "1") {
        return "0";
    }
    
    int N = S.length();
    string temp = S; // Work on a copy of the string
    
    int i = N - 1; // Start from the rightmost digit (least significant)
    while (i >= 0) {
        if (temp[i] > '0') {
            // If the current digit is greater than '0', decrement it
            temp[i]--;
            // All digits to the right (if any) become '9'
            for (int j = i + 1; j < N; ++j) {
                temp[j] = '9';
            }
            // Finished the subtraction process, break the loop
            break; 
        } else { // temp[i] == '0'
            // If the current digit is '0', set it to '9' and continue borrowing from the left
            temp[i] = '9'; 
            i--; // Move to the next digit to the left
        }
    }

    // Check if the leading digit became '0' after borrowing (e.g., "1000" -> "0999")
    // If so, and the original number had more than one digit, remove the leading '0'
    if (temp[0] == '0' && N > 1) {
        return temp.substr(1); // Return the substring starting from index 1
    } else {
        // Otherwise, return the modified string
        return temp;
    }
}


int main() {
    // Optimize C++ standard input/output operations for speed
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    string A, B; // Input numbers A and B as strings
    cin >> A >> B;

    // Calculate count of numbers satisfying the condition up to B
    long long ans_B = count_N(B);
    
    // Calculate A-1 as a string
    string A_minus_1 = subtract_one(A);
    
    // Calculate count of numbers satisfying the condition up to A-1
    long long ans_A_minus_1 = count_N(A_minus_1);

    // The final answer for the range [A, B] is count(B) - count(A-1)
    // Perform calculation modulo MOD, ensuring non-negative result
    long long final_ans = (ans_B - ans_A_minus_1 + MOD) % MOD;
    
    // Output the final answer
    cout << final_ans << endl;

    return 0;
}
0