File size: 10,396 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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Modified chain: d POP instructions + 1 HALT, but with more complex structure
// Instead of each POP using a unique char, we can have some POPs share chars
// or use multiple chars per level.
//
// Key idea: In a standard chain with d=26, max T = 2^27-1 = 134217727
// We need T = 150994941 (target 1) or 150994939 (target 2)
// That's about 12.5% more than the chain max.
//
// What if we use one of the 26 POP slots more effectively?
// E.g., one POP instruction that, instead of doubling, triples the count?
//
// For a standard chain POP at level j with gy[j]=0:
//   solve(j, x) for x != (j+1): push (j+1), go to 0, solve(0, j+1) = chain_steps(j), then solve(chain_ret(j), x)
//   This gives: steps(j, x) = 1 + chain_steps(0 to j-1) + steps(next, x)
//   And chain_ret(j) = j+1 (the next instruction after clearing the push)
//
// What if instead of pushing a char that gets popped by this same instruction,
// we push a char that needs to traverse MULTIPLE instructions before being popped?
//
// Actually, the issue is different. In the checker, the recursion is:
//   solve(i, x): for POP i, if x != pop_char:
//     (j, u) = solve(push_goto, push_char)  -- "evaluate the push"
//     (k, v) = solve(j, x)                  -- "then handle x at the return point"
//     result = (k, u + v + 1)
//
// For a standard chain with gy[j]=0: solve(0, push_char) traverses the entire subchain from 0.
// The push_char is (j+1), which doesn't match any pop_char of instructions 0..j-1 (they pop 1..j).
// So solve(0, j+1) pushes at every level, creating a tree of depth j.
//
// What if we use 2 different chars at one level? E.g., have POP j pop char j+1,
// but push TWO different chars (at two different instructions). But each POP can only push one char.
//
// Alternative: use a non-chain goto. E.g., POP j pushes char c and goes to some instruction k != 0.
// If k goes to an instruction that pushes more, we get more steps.
//
// Actually, the fundamental issue: for d=26 chain, the computation is exact (< P).
// So the step count is literally bounded by 2^27-1. No modular tricks.
//
// For the step count to exceed 2^27-1 with 27 instructions, we NEED non-chain structure
// that creates more than 2^27-1 steps. This requires the dp DAG to have more paths.
//
// With n=27 instructions and alphabet up to 1024, we have 27*1025 = 27675 states.
// The step count grows like Fibonacci/exponential over the DAG.
// A DAG on 27675 nodes can have step counts up to... 2^27675? No, it depends on the structure.
//
// Actually, the computation does: step[state] = 1 + step[child1] + step[child2]
// where child1 and child2 are other states. If the DAG is a binary tree of depth D,
// step count = 2^D. The maximum depth is bounded by the number of states = 27675.
// So max step count could be astronomical (2^27675). But we need it mod P.
//
// The key: with enough states (via larger alphabet), we can create deep recursion trees
// that wrap around mod P to hit any target!
//
// So the question becomes: what's the minimum n such that n * (max_char + 1) provides
// enough states to hit our target?
//
// For a chain with d instructions and alphabet A, the "effective depth" is d * A
// (each instruction can be visited with A different stack tops).
// We need enough depth to wrap around mod P.
//
// With n instructions and alphabet A:
// - Number of states: n * (A+1)
// - If we can create a linear chain of these states, step count = 2^(n*(A+1))
// - We need 2^(n*(A+1)) >= P ≈ 10^9, so n*(A+1) >= 30
//
// With n=4, A=7: 4*8=32 states >= 30. So theoretically n=4 can achieve any target mod P!
// But we need to find the right program structure.

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

constexpr long long P = 998244353;

int n_instr;
int maxChar;
int type_arr[30];
int pop_char_arr[30], goto1_arr[30], push_char_arr[30], goto2_arr[30];

// dp indexed by [instr][char], where char 0=empty, 1..maxChar
optional<pair<int, long long>> dp[30][1030];
bool vis[30][1030];
bool infinite_flag;

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

    if (type_arr[i] == 0) {
        if (x == pop_char_arr[i]) {
            dp[i][x] = {goto1_arr[i], 1LL};
        } else {
            auto [j, u] = solve(goto2_arr[i], push_char_arr[i]);
            if (infinite_flag) return {-1, 0};
            auto [k, v] = solve(j, x);
            if (infinite_flag) 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_arr[i], push_char_arr[i]);
            if (infinite_flag) return {-1, 0};
            auto [k, v] = solve(j, x);
            if (infinite_flag) return {-1, 0};
            dp[i][x] = {k, (u + v + 1) % P};
        }
    }
    return dp[i][x].value();
}

long long evaluate() {
    for (int i = 0; i < n_instr; i++)
        for (int j = 0; j <= maxChar; j++) {
            dp[i][j].reset();
            vis[i][j] = false;
        }
    infinite_flag = false;
    auto [fi, steps] = solve(0, 0);
    if (infinite_flag) return -1;
    return steps;
}

int main(int argc, char* argv[]) {
    if (argc < 4) {
        fprintf(stderr, "Usage: %s <target> <n> <alpha>\n", argv[0]);
        return 1;
    }
    long long target = atoll(argv[1]);
    n_instr = atoi(argv[2]);
    maxChar = atoi(argv[3]);

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

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

    // Save best
    int best_type[30], best_pop[30], best_g1[30], best_push[30], best_g2[30];
    bool found = false;
    int restarts = 0;

    while (elapsed_ms() < 120000 && !found) {
        restarts++;
        // Random init: ensure at least one HALT, prefer mostly POP
        for (int i = 0; i < n_instr; i++) {
            if (i == n_instr - 1) type_arr[i] = 1; // last is HALT
            else type_arr[i] = 0; // POP

            if (type_arr[i] == 0) {
                pop_char_arr[i] = 1 + rng() % maxChar;
                goto1_arr[i] = rng() % n_instr;
                push_char_arr[i] = 1 + rng() % maxChar;
                goto2_arr[i] = rng() % n_instr;
            } else {
                push_char_arr[i] = 1 + rng() % maxChar;
                goto2_arr[i] = rng() % n_instr;
            }
        }

        long long T = evaluate();
        if (T < 0) continue;
        if (T == target) {
            found = true;
            memcpy(best_type, type_arr, sizeof(best_type));
            memcpy(best_pop, pop_char_arr, sizeof(best_pop));
            memcpy(best_g1, goto1_arr, sizeof(best_g1));
            memcpy(best_push, push_char_arr, sizeof(best_push));
            memcpy(best_g2, goto2_arr, sizeof(best_g2));
            break;
        }

        // Hill climbing
        for (int iter = 0; iter < 200000 && !found; iter++) {
            // Save
            int sv_type[30], sv_pop[30], sv_g1[30], sv_push[30], sv_g2[30];
            memcpy(sv_type, type_arr, sizeof(sv_type));
            memcpy(sv_pop, pop_char_arr, sizeof(sv_pop));
            memcpy(sv_g1, goto1_arr, sizeof(sv_g1));
            memcpy(sv_push, push_char_arr, sizeof(sv_push));
            memcpy(sv_g2, goto2_arr, sizeof(sv_g2));

            // Mutate one instruction
            int i = rng() % n_instr;
            int what = rng() % 6;
            if (what == 0 && i < n_instr - 1) {
                // Change type (rarely)
                // skip for now, keep structure stable
            }
            if (what == 1 && type_arr[i] == 0) push_char_arr[i] = 1 + rng() % maxChar;
            else if (what == 2) goto2_arr[i] = rng() % n_instr;
            else if (what == 3 && type_arr[i] == 0) goto1_arr[i] = rng() % n_instr;
            else if (what == 4 && type_arr[i] == 0) pop_char_arr[i] = 1 + rng() % maxChar;
            else if (what == 5) {
                if (type_arr[i] == 0) push_char_arr[i] = 1 + rng() % maxChar;
                else push_char_arr[i] = 1 + rng() % maxChar;
            }

            long long nT = evaluate();
            if (nT < 0) {
                memcpy(type_arr, sv_type, sizeof(sv_type));
                memcpy(pop_char_arr, sv_pop, sizeof(sv_pop));
                memcpy(goto1_arr, sv_g1, sizeof(sv_g1));
                memcpy(push_char_arr, sv_push, sizeof(sv_push));
                memcpy(goto2_arr, sv_g2, sizeof(sv_g2));
                continue;
            }

            if (nT == target) {
                found = true;
                memcpy(best_type, type_arr, sizeof(best_type));
                memcpy(best_pop, pop_char_arr, sizeof(best_pop));
                memcpy(best_g1, goto1_arr, sizeof(best_g1));
                memcpy(best_push, push_char_arr, sizeof(best_push));
                memcpy(best_g2, goto2_arr, sizeof(best_g2));
                break;
            }

            long long nd = min((nT - target + P) % P, (target - nT + P) % P);
            long long od = min((T - target + P) % P, (target - T + P) % P);

            if (nd <= od) {
                T = nT;
            } else {
                memcpy(type_arr, sv_type, sizeof(sv_type));
                memcpy(pop_char_arr, sv_pop, sizeof(sv_pop));
                memcpy(goto1_arr, sv_g1, sizeof(sv_g1));
                memcpy(push_char_arr, sv_push, sizeof(sv_push));
                memcpy(goto2_arr, sv_g2, sizeof(sv_g2));
            }
        }

        if (restarts % 50 == 0) {
            fprintf(stderr, "restart=%d elapsed=%ldms\n", restarts, elapsed_ms());
        }
    }

    if (found) {
        fprintf(stderr, "FOUND n=%d alpha=%d after %d restarts\n", n_instr, maxChar, restarts);
        printf("%d\n", n_instr);
        for (int i = 0; i < n_instr; i++) {
            if (best_type[i] == 0) {
                printf("POP %d GOTO %d PUSH %d GOTO %d\n",
                    best_pop[i], best_g1[i]+1, best_push[i], best_g2[i]+1);
            } else {
                printf("HALT PUSH %d GOTO %d\n", best_push[i], best_g2[i]+1);
            }
        }
    } else {
        fprintf(stderr, "NOT FOUND n=%d alpha=%d after %d restarts\n", n_instr, maxChar, restarts);
    }

    return 0;
}