#include using namespace std; // Build the ADFA for [L,R] and perform proper minimization // Then try cross-depth merging and other optimizations int L = 577837, R = 979141; int main() { // Both L and R are 20-bit numbers int bits = 20; assert(L >= (1 << 19) && R < (1 << 20)); // The ADFA has 21 layers (depth 0 = START, depth 20 = END) // At each depth d, after reading d bits, we have a "state" that represents // the set of suffixes that can follow to complete a valid number in [L,R] // State at depth d after reading prefix p (a d-bit number, first bit is 1): // The set of (20-d)-bit suffixes s such that p*2^(20-d) + s is in [L,R] // i.e., L <= p*2^(20-d) + s <= R // i.e., max(0, L - p*2^(20-d)) <= s <= min(2^(20-d)-1, R - p*2^(20-d)) // So the state is characterized by (lo, hi) where lo and hi are the bounds on the suffix // lo = max(0, L - p*2^(20-d)) // hi = min(2^(20-d)-1, R - p*2^(20-d)) // Two prefixes at the same depth have the same state iff they have the same (lo, hi) // Let's enumerate all distinct (lo, hi) pairs at each depth // At depth d, valid prefixes p satisfy: 2^(d-1) <= p < 2^d (first bit is 1) // And the range of complete numbers is [L, R] // So: p*2^(20-d) + s in [L,R], s in [0, 2^(20-d)-1] // We need: p*2^(20-d) <= R and (p+1)*2^(20-d) - 1 >= L // i.e., p <= R >> (20-d) and p >= ceil(L / 2^(20-d)) ... roughly cout << "Analyzing ADFA states for L=" << L << " R=" << R << " bits=" << bits << endl; // For each depth, collect all distinct (lo, hi) suffix range pairs // Also record transition structure struct State { int lo, hi; // suffix range [lo, hi] at remaining bits k int k; // remaining bits }; // Map from (k, lo, hi) to state id map, int> state_id; int next_id = 0; // Transitions: state_id -> (child_on_0, child_on_1) where -1 means no edge vector> trans; // BFS/DFS from START function get_state = [&](int k, int lo, int hi) -> int { if (lo > hi) return -1; auto key = make_tuple(k, lo, hi); auto it = state_id.find(key); if (it != state_id.end()) return it->second; int id = next_id++; state_id[key] = id; trans.push_back({-1, -1}); if (k == 0) { // END state, no transitions return id; } int mid = 1 << (k - 1); // On bit 0: suffix starts with 0, so s = 0*2^(k-1) + s' // New range: lo <= s' <= min(hi, mid-1) int lo0 = lo, hi0 = min(hi, mid - 1); trans[id][0] = get_state(k - 1, lo0, hi0); // On bit 1: suffix starts with 1, so s = mid + s' // New range: max(lo, mid) - mid <= s' <= hi - mid int lo1 = max(lo, mid) - mid, hi1 = hi - mid; trans[id][1] = get_state(k - 1, lo1, hi1); return id; }; // START: depth 0, first bit must be 1 // After reading bit 1, we're at depth 1 with prefix = 1 // Remaining bits: 19 // suffix range: max(0, L - 1*2^19) <= s <= min(2^19-1, R - 1*2^19) int base = 1 << 19; // 524288 int lo_start = max(0, L - base); int hi_start = min((1 << 19) - 1, R - base); cout << "Start suffix range: [" << lo_start << ", " << hi_start << "]" << endl; int start_child = get_state(19, lo_start, hi_start); // +1 for START, +1 for END (already counted) // START is separate (connects to start_child via edge weight 1) // Actually START is a special node not counted in get_state cout << "\nTotal distinct states (excluding START): " << next_id << endl; cout << "Total nodes (including START): " << next_id + 1 << endl; // Print states by depth (remaining bits k) map>> by_k; for (auto& [key, id] : state_id) { auto [k, lo, hi] = key; by_k[k].push_back({lo, hi}); } cout << "\nStates by depth (remaining bits k -> number of states):" << endl; int total = 1; // START for (int k = 19; k >= 0; k--) { auto& states = by_k[k]; sort(states.begin(), states.end()); cout << "k=" << k << " (depth " << (20-k) << "): " << states.size() << " states" << endl; for (auto& [lo, hi] : states) { auto it = state_id.find({k, lo, hi}); int id = it->second; cout << " id=" << id << " [" << lo << "," << hi << "]"; if (k > 0) { cout << " -> 0:" << trans[id][0] << " 1:" << trans[id][1]; } cout << endl; } total += states.size(); } cout << "\nTotal: " << total << endl; // Now check: are there any states at different depths that have identical // transition structures (same children)? cout << "\n=== Cross-depth analysis ===" << endl; // Compute the "signature" of each state bottom-up // Signature = the set of numbers reachable from this state // Two states can be merged if they have the same signature AND // can be made consistent in the DAG // Actually, for merging, we need states with identical sub-DAGs // Compute hash of sub-DAG rooted at each state map state_hash; function compute_hash = [&](int id) -> long long { if (state_hash.count(id)) return state_hash[id]; if (id == -1) return -1; long long h0 = compute_hash(trans[id][0]); long long h1 = compute_hash(trans[id][1]); // Simple hash long long h = h0 * 1000000007LL + h1 * 998244353LL + 12345; return state_hash[id] = h; }; for (auto& [key, id] : state_id) { compute_hash(id); } // Group states by hash map> by_hash; for (auto& [key, id] : state_id) { by_hash[state_hash[id]].push_back(id); } cout << "\nStates grouped by sub-DAG hash:" << endl; int mergeable = 0; for (auto& [h, ids] : by_hash) { if (ids.size() > 1) { cout << "Hash " << h << ": states"; for (int id : ids) { // Find the key for this id for (auto& [key, sid] : state_id) { if (sid == id) { auto [k, lo, hi] = key; cout << " (k=" << k << ",[" << lo << "," << hi << "])"; break; } } } cout << endl; mergeable += ids.size() - 1; } } cout << "Potentially mergeable: " << mergeable << " states" << endl; // Now let's try to actually build a minimized DAG with cross-depth merging // using structural equivalence // Two nodes are structurally equivalent if: // - Both are END (k=0, lo=0, hi=0), or // - They have the same transition structure after merging: // trans[a][0] is equivalent to trans[b][0] AND trans[a][1] is equivalent to trans[b][1] // This is exactly what the hash captures (assuming no collisions) // Let's verify with exact comparison // Build equivalence classes bottom-up map eq_class; // state_id -> equivalence class id int n_classes = 0; // First, assign classes to END state(s) // Then go up level by level // Process by k (remaining bits), starting from k=0 for (int k = 0; k <= 19; k++) { // Get all states at this level map, int> sig_to_class; // (class_of_child0, class_of_child1) -> class for (auto& [key, id] : state_id) { auto [kk, lo, hi] = key; if (kk != k) continue; if (k == 0) { // END state - all END states are equivalent if (!sig_to_class.count({-1, -1})) { sig_to_class[{-1, -1}] = n_classes++; } eq_class[id] = sig_to_class[{-1, -1}]; } else { int c0 = (trans[id][0] == -1) ? -1 : eq_class[trans[id][0]]; int c1 = (trans[id][1] == -1) ? -1 : eq_class[trans[id][1]]; auto sig = make_pair(c0, c1); if (!sig_to_class.count(sig)) { sig_to_class[sig] = n_classes++; } eq_class[id] = sig_to_class[sig]; } } } cout << "\n=== After cross-depth minimization ===" << endl; cout << "Number of equivalence classes: " << n_classes << endl; cout << "Total nodes (with START): " << n_classes + 1 << endl; // Print which states got merged map> class_members; for (auto& [id, cls] : eq_class) { class_members[cls].push_back(id); } int merged_count = 0; for (auto& [cls, members] : class_members) { if (members.size() > 1) { cout << "Class " << cls << " merges:"; for (int id : members) { for (auto& [key, sid] : state_id) { if (sid == id) { auto [k, lo, hi] = key; cout << " (k=" << k << ",[" << lo << "," << hi << "])"; break; } } } cout << endl; merged_count += members.size() - 1; } } cout << "Total nodes saved by cross-depth merging: " << merged_count << endl; return 0; }