File size: 4,308 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <bits/stdc++.h>
using namespace std;

struct Graph {
    vector<vector<pair<int,int>>> adj; // 1-based
    Graph() { adj.resize(1); } // dummy 0
    int new_node() {
        adj.emplace_back();
        return (int)adj.size() - 1;
    }
    void add_edge(int u, int v, int w) {
        adj[u].push_back({v, w});
    }
    int size() const { return (int)adj.size() - 1; }
};

static Graph G;
static int S, T;

static vector<int> Lbits, Rbits;
static int lenL, lenR;

static vector<int> freeNode;   // by rem
static vector<int> lowNode;    // by pos
static vector<int> upNode;     // by pos
static vector<int> bothNode;   // by pos (when lenL == lenR)

int getFree(int rem) {
    if (rem == 0) return T;
    if (rem < (int)freeNode.size() && freeNode[rem] != 0) return freeNode[rem];
    int needSize = rem + 1;
    if ((int)freeNode.size() < needSize) freeNode.resize(needSize, 0);
    int id = G.new_node();
    freeNode[rem] = id;
    int to = getFree(rem - 1);
    G.add_edge(id, to, 0);
    G.add_edge(id, to, 1);
    return id;
}

int getLower(int pos) {
    if (pos >= lenL) return T;
    if (lowNode[pos] != 0) return lowNode[pos];
    int id = G.new_node();
    lowNode[pos] = id;
    int b = Lbits[pos];
    if (b == 0) {
        // 0 keeps tight
        int to_tight = getLower(pos + 1);
        G.add_edge(id, to_tight, 0);
        // 1 breaks tight -> free with remaining
        int rem = lenL - pos - 1;
        int to_free = getFree(rem);
        G.add_edge(id, to_free, 1);
    } else { // b == 1
        int to_tight = getLower(pos + 1);
        G.add_edge(id, to_tight, 1);
    }
    return id;
}

int getUpper(int pos) {
    if (pos >= lenR) return T;
    if (upNode[pos] != 0) return upNode[pos];
    int id = G.new_node();
    upNode[pos] = id;
    int b = Rbits[pos];
    if (b == 1) {
        // 1 keeps tight
        int to_tight = getUpper(pos + 1);
        G.add_edge(id, to_tight, 1);
        // 0 breaks tight -> free with remaining
        int rem = lenR - pos - 1;
        int to_free = getFree(rem);
        G.add_edge(id, to_free, 0);
    } else { // b == 0
        int to_tight = getUpper(pos + 1);
        G.add_edge(id, to_tight, 0);
    }
    return id;
}

int getBoth(int pos) { // only when lenL == lenR
    int len = lenR;
    if (pos >= len) return T;
    if (bothNode[pos] != 0) return bothNode[pos];
    int id = G.new_node();
    bothNode[pos] = id;
    int lb = Lbits[pos], ub = Rbits[pos];
    if (lb == ub) {
        int to = getBoth(pos + 1);
        G.add_edge(id, to, lb);
    } else { // lb=0, ub=1
        int to_low = getLower(pos + 1);
        int to_up  = getUpper(pos + 1);
        G.add_edge(id, to_low, 0);
        G.add_edge(id, to_up, 1);
    }
    return id;
}

vector<int> toBitsMSB(int x) {
    vector<int> b;
    while (x > 0) {
        b.push_back(x & 1);
        x >>= 1;
    }
    reverse(b.begin(), b.end());
    if (b.empty()) b.push_back(0);
    return b;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int L, R;
    if (!(cin >> L >> R)) return 0;

    Lbits = toBitsMSB(L);
    Rbits = toBitsMSB(R);
    lenL = (int)Lbits.size();
    lenR = (int)Rbits.size();

    // init graph with start and sink
    S = G.new_node();
    T = G.new_node();

    // prepare containers
    freeNode.resize(lenR + 1, 0);
    lowNode.assign(lenL + 1, 0);
    upNode.assign(lenR + 1, 0);
    if (lenL == lenR) bothNode.assign(lenR + 1, 0);

    // Build edges from start
    // First bit must be 1 always.
    if (lenL == lenR) {
        int to = getBoth(1);
        G.add_edge(S, to, 1);
    } else {
        // len == lenL (lower bound)
        int toLow = getLower(1);
        G.add_edge(S, toLow, 1);

        // lengths between lenL+1 and lenR-1 (free)
        for (int len = lenL + 1; len <= lenR - 1; ++len) {
            int toFree = getFree(len - 1); // remaining after first '1'
            G.add_edge(S, toFree, 1);
        }

        // len == lenR (upper bound)
        int toUp = getUpper(1);
        G.add_edge(S, toUp, 1);
    }

    // Output
    int n = G.size();
    cout << n << "\n";
    for (int i = 1; i <= n; ++i) {
        cout << (int)G.adj[i].size();
        for (auto &e : G.adj[i]) {
            cout << " " << e.first << " " << e.second;
        }
        cout << "\n";
    }
    return 0;
}