結果
| 問題 |
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 |
ソースコード
#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;
}
qwewe