// 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 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> dp[30][1030]; bool vis[30][1030]; bool infinite_flag; pair 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 \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::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; }