File size: 4,837 Bytes
1fd0050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#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;
}