File size: 3,775 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
#include <bits/stdc++.h>
using namespace std;

int main() {
    int L = 577837, R = 979141;

    struct Edge { int to, w; };
    vector<vector<Edge>> adj;
    int n = 0;
    auto nn = [&]() -> int { adj.emplace_back(); return n++; };

    int END = nn();
    int START = nn();

    map<int,int> fc;
    fc[0] = END;
    function<int(int)> getF = [&](int k) -> int {
        if (fc.count(k)) return fc[k];
        int ch = getF(k-1), u = nn();
        adj[u].push_back({ch, 0});
        adj[u].push_back({ch, 1});
        return fc[k] = u;
    };

    map<tuple<int,int,int>,int> rc;
    function<int(int,int,int)> gr = [&](int k, int lo, int hi) -> int {
        if (lo > hi) return -1;
        if (k == 0) return (lo == 0 && hi == 0) ? END : -1;
        if (lo == 0 && hi == (1<<k)-1) return getF(k);
        auto key = make_tuple(k, lo, hi);
        auto it = rc.find(key);
        if (it != rc.end()) return it->second;
        int mid = 1 << (k-1);
        int left = -1, right = -1;
        if (lo <= min(hi, mid-1))
            left = gr(k-1, lo, min(hi, mid-1));
        if (max(lo, mid) <= hi)
            right = gr(k-1, max(lo, mid) - mid, hi - mid);
        if (left == -1 && right == -1) return rc[key] = -1;
        int u = nn();
        if (left != -1) adj[u].push_back({left, 0});
        if (right != -1) adj[u].push_back({right, 1});
        return rc[key] = u;
    };

    int lenL = 32 - __builtin_clz(L), lenR = 32 - __builtin_clz(R);
    for (int len = lenL; len <= lenR; len++) {
        int rs = 1 << (len-1), re = (1<<len)-1;
        int cL = max(L, rs), cR = min(R, re);
        if (cL > cR) continue;
        int target = (len == 1) ? END : gr(len-1, cL - rs, cR - rs);
        if (target != -1) adj[START].push_back({target, 1});
    }

    // Compute signatures bottom-up
    vector<bool> reach(n, false);
    queue<int> q;
    q.push(START); reach[START] = true;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (auto& e : adj[u]) {
            if (!reach[e.to]) { reach[e.to] = true; q.push(e.to); }
        }
    }

    // Topological sort
    vector<int> indeg(n, 0);
    for (int u = 0; u < n; u++) {
        if (!reach[u]) continue;
        for (auto& e : adj[u]) indeg[e.to]++;
    }
    vector<int> topo;
    for (int u = 0; u < n; u++)
        if (reach[u] && indeg[u] == 0) q.push(u);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        topo.push_back(u);
        for (auto& e : adj[u]) if (--indeg[e.to] == 0) q.push(e.to);
    }
    reverse(topo.begin(), topo.end());

    // Compute canonical subtree signature
    map<vector<pair<int,int>>, vector<int>> sig_groups;
    vector<int> rep(n, -1);
    
    for (int u : topo) {
        if (!reach[u]) continue;
        vector<pair<int,int>> sig;
        for (auto& e : adj[u]) {
            sig.push_back({rep[e.to], e.w});
        }
        sort(sig.begin(), sig.end());
        
        if (sig_groups.count(sig)) {
            rep[u] = sig_groups[sig][0];
            sig_groups[sig].push_back(u);
        } else {
            rep[u] = u;
            sig_groups[sig] = {u};
        }
    }

    // Count unique reps
    set<int> unique_reps;
    for (int u = 0; u < n; u++)
        if (reach[u]) unique_reps.insert(rep[u]);
    
    cout << "Original reachable: " << count(reach.begin(), reach.end(), true) << endl;
    cout << "After merging: " << unique_reps.size() << endl;
    
    // Show groups with >1 member
    for (auto& [sig, group] : sig_groups) {
        if (group.size() > 1) {
            cout << "Merged group: ";
            for (int u : group) cout << u << " ";
            cout << " sig: ";
            for (auto& [c, w] : sig) cout << "(" << c << "," << w << ")";
            cout << endl;
        }
    }

    return 0;
}