JustinTX's picture
Add files using upload-large-folder tool
1fd0050 verified
#include <bits/stdc++.h>
using namespace std;
// BDD approach: build a reduced ordered BDD for the predicate L <= x <= R
// where x is a 20-bit number.
// Each BDD node corresponds to a bit position and has two children (0 and 1).
// Terminal nodes: TRUE (accept) and FALSE (reject).
// We only need paths that reach TRUE, so FALSE branches are simply absent.
// Key difference from current approach: BDD sharing can happen across bit levels
// if two sub-functions are identical.
// Actually, this is EXACTLY what the current solution does with the recursive
// range decomposition and memoization. The map<tuple<int,int,int>,int> rc
// is essentially BDD node caching.
// But BDD has a crucial additional optimization: if two sub-functions at
// DIFFERENT bit positions are identical, they can share a node.
// In standard ROBDD, nodes are ordered by variable, so a variable-k node
// can only point to variable-(k-1) nodes. No cross-level sharing.
// In our problem, the DAG doesn't need to be a BDD. It just needs to be
// a DAG where paths spell out binary representations of numbers in [L,R].
// Can we build a "relaxed" BDD where a node at level k can point to
// a node at level k-2 (skipping a level)?
// If so, the skipped bit is ALWAYS a specific value (constrained path).
// But then some paths have 19 edges and others have 20. Wrong!
// Unless we ADD dummy "pass-through" nodes to equalize path lengths.
// But that would ADD nodes, not save them.
// OK here's another idea. What if some paths have FEWER edges but
// the first bit is still 1 and the number is valid?
// For example, a number like 577837 is 20 bits. But what about the
// number 999141? Also 20 bits.
// ALL numbers in [577837, 979141] are 20 bits. So all paths must be 20 edges.
// I think we're stuck at 55. Let me try one more thing: a SAT-based search
// for tiny cases to see if cross-depth sharing ever helps.
int L, R;
struct Edge { int to, w; };
vector<vector<Edge>> adj;
int n_nodes;
vector<int> indeg, outdeg;
bool verify(int L, int R) {
int start = -1, end_node = -1;
for (int i = 1; i <= n_nodes; i++) {
if (indeg[i] == 0) start = i;
if (outdeg[i] == 0) end_node = i;
}
if (start < 0 || end_node < 0) return false;
// Check no leading zeros
for (auto& e : adj[start]) if (e.w == 0) return false;
int cnt = 0;
vector<int> vis(R + 1, 0);
bool error = false;
function<void(int, int)> dfs = [&](int x, int val) {
if (error) return;
if (outdeg[x] == 0) {
cnt++;
if (cnt > 1000000 || val < L || val > R) { error = true; return; }
vis[val]++;
if (vis[val] > 1) { error = true; return; }
return;
}
for (auto& e : adj[x]) dfs(e.to, val * 2 + e.w);
};
dfs(start, 0);
if (error) return false;
for (int i = L; i <= R; i++) if (vis[i] == 0) return false;
return true;
}
// For small ranges, try to find if the ADFA can be beaten
void solve_optimal(int L, int R) {
// Build ADFA
map<tuple<int,int,int>, int> state_id;
int next_id = 0;
vector<array<int,2>> trans;
function<int(int, int, int)> 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) return id;
int mid = 1 << (k - 1);
trans[id][0] = get_state(k - 1, lo, min(hi, mid - 1));
trans[id][1] = get_state(k - 1, max(lo, mid) - mid, hi - mid);
return id;
};
int bits = 0;
int tmp = R;
while (tmp > 0) { bits++; tmp >>= 1; }
int base = 1 << (bits - 1);
get_state(bits - 1, max(0, L - base), min(base - 1, R - base));
int adfa_nodes = next_id + 1; // +1 for START
// Now try to build with fewer nodes using brute force
// (only feasible for tiny ranges)
// Skip this for large ranges
cout << "L=" << L << " R=" << R << " bits=" << bits << " ADFA=" << adfa_nodes << endl;
}
int main() {
// Test on small cases to see if ADFA is always optimal
cout << "Testing small cases..." << endl;
for (int l = 1; l <= 30; l++) {
for (int r = l; r <= 30; r++) {
int bits = 0;
int tmp = r;
while (tmp > 0) { bits++; tmp >>= 1; }
int bitsL = 0;
tmp = l;
while (tmp > 0) { bitsL++; tmp >>= 1; }
// Only test same-bit-length ranges
if (bitsL == bits) {
solve_optimal(l, r);
}
}
}
// Now for the actual problem
cout << "\nActual problem:" << endl;
solve_optimal(577837, 979141);
return 0;
}