File size: 7,766 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Ladder construction: try to get T > 2^(d+1)-1 with d POPs
// by using non-standard push chars that create cross-references.
//
// Consider d=26 (n=27). Standard chain max T = 2^27-1 = 134M.
// We need T = 150M.
//
// What if POP j pushes char (j+1) [standard] but goes to a deeper goto?
// In the standard chain, solve(j, x) for x != j+1:
//   cost = 1 + solve(gy[j], j+1).steps + solve(j+1, x).steps
//   where solve(gy[j], j+1) traverses from gy[j] to j (cost depends on gy[j])
//   and solve(j+1, x) continues the chain.
//
// With gy[j]=0 for all j: solve(j, x) = 2^(j+1) * solve_x_factor + stuff
// Each level doubles.
//
// What if some instruction pushes a char that causes the resolve step
// to call MULTIPLE pops? E.g., if POP 5 pushes char 3 instead of char 6,
// then solve(gy[5], 3) goes to gy[5], and char 3 gets popped by POP 2
// (which pops char 3). But to reach POP 2 from gy[5], we might traverse
// through POPs 0-2, each adding their own costs.
//
// Wait, that's the same as setting gy[5] to a different value.
// Actually no - the push_char determines WHICH POP consumes the push.
// In standard chain: push_char = j+1, consumed by POP j itself.
// If push_char = 3 (for POP 5), consumed by POP 2.
// Then solve(gy[5], 3) returns at POP 2's goto = 3.
// Then solve(3, x) continues from POP 3.
// But we skipped POPs 3, 4! The resolve step only went to POP 2.
//
// Actually this makes the resolve step SHORTER, not longer.
// To make it LONGER, we want push_char to NOT match any POP's pop_char.
// If push_char = d+1 (a char no POP pops), then the char propagates all the way
// to HALT. HALT processes it by pushing something else and recursing.
// This could create much deeper recursion!
//
// Let me try: d POP instructions that all pop chars 1..d.
// Some POPs push char d+1 (which NO POP pops).
// HALT pushes char 1 and goes to some instruction.
// When char d+1 reaches HALT, HALT pushes char 1 and continues.
// Char 1 gets popped by POP 0, which goes to POP 1.
// So the "resolve" for char d+1 goes through the entire chain AND the HALT.

#include <bits/stdc++.h>
using namespace std;

constexpr long long P = 998244353;

// General program: n instructions
// Each instruction is POP or HALT with given params
// Solve by checker's recursive method

struct Program {
    int n;
    int type[30];     // 0=POP, 1=HALT
    int pop_c[30];    // POP char (for POP)
    int goto1[30];    // goto on pop match (for POP)
    int push_c[30];   // push char
    int goto2[30];    // goto on push

    optional<pair<int, long long>> dp[30][40];
    bool vis[30][40];
    bool inf;
    int maxC;

    void clear_dp() {
        for (int i = 0; i < n; i++)
            for (int j = 0; j <= maxC; j++) {
                dp[i][j].reset();
                vis[i][j] = false;
            }
        inf = false;
    }

    pair<int, long long> solve(int i, int x) {
        if (dp[i][x]) return dp[i][x].value();
        if (vis[i][x]) { inf = true; return {-1, 0}; }
        vis[i][x] = true;

        if (type[i] == 0) {
            if (x == pop_c[i]) {
                dp[i][x] = {goto1[i], 1LL};
            } else {
                auto [j, u] = solve(goto2[i], push_c[i]);
                if (inf) return {-1, 0};
                auto [k, v] = solve(j, x);
                if (inf) return {-1, 0};
                dp[i][x] = {k, (u + v + 1) % P};
            }
        } else {
            if (x == 0) {
                dp[i][x] = {-1, 1LL};
            } else {
                auto [j, u] = solve(goto2[i], push_c[i]);
                if (inf) return {-1, 0};
                auto [k, v] = solve(j, x);
                if (inf) return {-1, 0};
                dp[i][x] = {k, (u + v + 1) % P};
            }
        }
        return dp[i][x].value();
    }

    long long eval() {
        clear_dp();
        auto [fi, steps] = solve(0, 0);
        if (inf) return -1;
        return steps;
    }
};

int main() {
    long long target1 = 150994941;
    long long target2 = 150994939;

    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

    // Try specific construction: d POP + 1 HALT
    // POP j: pop char (j+1), goto (j+2) [next POP or HALT]
    //        push char push_c[j], goto push_g[j]
    // HALT d: push char halt_c, goto halt_g
    //
    // Chars used: 1..d for pop_chars, plus additional chars for push.
    // If push_c[j] > d: no POP pops this char, so it propagates to HALT.
    // HALT then pushes halt_c and continues.
    //
    // This creates a more complex recursion than a simple chain.

    for (int d = 15; d <= 26; d++) {
        int n = d + 1;
        Program prog;
        prog.n = n;
        int maxPushChar = d + 3; // allow a few extra chars
        prog.maxC = maxPushChar;

        fprintf(stderr, "=== d=%d (n=%d, maxC=%d) ===\n", d, n, maxPushChar);

        auto dStart = chrono::steady_clock::now();
        auto dElapsed = [&]() {
            return chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - dStart).count();
        };

        bool found = false;

        for (int restart = 0; dElapsed() < 8000 && !found; restart++) {
            // Setup structure
            for (int j = 0; j < d; j++) {
                prog.type[j] = 0;
                prog.pop_c[j] = j + 1;
                prog.goto1[j] = j + 1; // standard: next instruction
                prog.push_c[j] = 1 + rng() % maxPushChar;
                prog.goto2[j] = rng() % n;
            }
            prog.type[d] = 1;
            prog.push_c[d] = 1 + rng() % maxPushChar;
            prog.goto2[d] = rng() % n;

            long long T = prog.eval();
            if (T < 0) continue;

            for (int iter = 0; iter < 300000 && !found; iter++) {
                if (T == target1 || T == target2) {
                    found = true;
                    fprintf(stderr, "FOUND! d=%d n=%d T=%lld\n", d, n, T);
                    printf("%d\n", n);
                    for (int j = 0; j < d; j++) {
                        printf("POP %d GOTO %d PUSH %d GOTO %d\n",
                            prog.pop_c[j], prog.goto1[j]+1, prog.push_c[j], prog.goto2[j]+1);
                    }
                    printf("HALT PUSH %d GOTO %d\n", prog.push_c[d], prog.goto2[d]+1);
                    break;
                }

                // Mutate
                int j = rng() % n;
                int what = rng() % 3;
                int sv_push = prog.push_c[j], sv_g2 = prog.goto2[j], sv_g1 = prog.goto1[j];

                if (what == 0) prog.push_c[j] = 1 + rng() % maxPushChar;
                else if (what == 1) prog.goto2[j] = rng() % n;
                else if (j < d) prog.goto1[j] = rng() % n; // vary pop goto too

                long long nT = prog.eval();
                if (nT < 0) {
                    prog.push_c[j] = sv_push; prog.goto2[j] = sv_g2; prog.goto1[j] = sv_g1;
                    continue;
                }

                long long nd1 = min((nT - target1 + P) % P, (target1 - nT + P) % P);
                long long od1 = min((T - target1 + P) % P, (target1 - T + P) % P);
                long long nd2 = min((nT - target2 + P) % P, (target2 - nT + P) % P);
                long long od2 = min((T - target2 + P) % P, (target2 - T + P) % P);
                long long best_nd = min(nd1, nd2);
                long long best_od = min(od1, od2);

                if (best_nd <= best_od) T = nT;
                else {
                    prog.push_c[j] = sv_push; prog.goto2[j] = sv_g2; prog.goto1[j] = sv_g1;
                }
            }

            if (restart % 200 == 0 && restart > 0)
                fprintf(stderr, "d=%d restart=%d elapsed=%ldms\n", d, restart, dElapsed());
        }

        if (found) return 0;
    }

    return 0;
}