#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// Minimal, fast BigInt optimized for base-N extraction and insertion
struct BigInt {
vector<uint32_t> limbs;
BigInt() {}
// Initialize from binary string (most significant bit first)
BigInt(const string& bin_str) {
for (char c : bin_str) {
mul_add(2, c - '0');
}
}
// M = M * m + a
void mul_add(uint32_t m, uint32_t a) {
uint64_t carry = a;
for (size_t i = 0; i < limbs.size(); ++i) {
uint64_t res = (uint64_t)limbs[i] * m + carry;
limbs[i] = (uint32_t)res;
carry = res >> 32;
}
if (carry > 0) {
limbs.push_back((uint32_t)carry);
}
}
// rem = M % d; M = M / d; return rem;
uint32_t div_mod(uint32_t d) {
uint64_t rem = 0;
for (int i = (int)limbs.size() - 1; i >= 0; --i) {
uint64_t cur = (rem << 32) + limbs[i];
limbs[i] = (uint32_t)(cur / d);
rem = cur % d;
}
// Trim leading zeros
while (!limbs.empty() && limbs.back() == 0) {
limbs.pop_back();
}
return (uint32_t)rem;
}
// Extract binary string of specific length
string to_bin_string(int length) {
string res;
BigInt temp = *this;
for (int i = 0; i < length; ++i) {
res.push_back((char)('0' + temp.div_mod(2)));
}
// Since div_mod extracts LSB first, we must reverse the result
reverse(res.begin(), res.end());
return res;
}
};
void solve() {
string identity;
if (!(cin >> identity)) return;
int n, t;
cin >> n >> t;
while (t--) {
string my_str;
cin >> my_str;
BigInt M_me(my_str);
int D = 0; // Score difference: My score - Opponent's score
int consecutive_P = 0; // Tie counter for termination
// Record states and opponent plays to reverse-decode at the end
// pair: {D_opp_before_round, opp_play}
vector<pair<int, char>> opp_history;
// --- PHASE 1: COMMUNICATION LOOP ---
while (true) {
char my_play = 'P';
// Extract symbol dynamically based on game state
if (D == 0) {
int slot = M_me.div_mod(3);
if (slot == 0) my_play = 'P';
else if (slot == 1) my_play = 'K';
else my_play = 'N';
}
else if (D == 1) { // I'm winning, I must coordinate to prevent hitting D=2
int slot = M_me.div_mod(4);
if (slot < 3) {
my_play = 'K'; // Lose the round, drop to D=0
M_me.mul_add(3, slot);
} else {
my_play = 'P'; // Tie the round, stay in D=1
}
}
else if (D == -1) { // I'm losing, I play deterministic 'P'
my_play = 'P';
}
cout << my_play << "\n";
cout.flush();
char opp_play;
cin >> opp_play;
opp_history.push_back({-D, opp_play});
// Count consecutive (P,P) in D=0 state to detect termination safely
if (D == 0 && my_play == 'P' && opp_play == 'P') {
consecutive_P++;
} else {
consecutive_P = 0;
}
// Update Game State
if (my_play != opp_play) {
if ((my_play == 'P' && opp_play == 'K') ||
(my_play == 'K' && opp_play == 'N') ||
(my_play == 'N' && opp_play == 'P')) {
D++;
} else {
D--;
}
}
// 30 consecutive P's guarantees (with probability > 1 - 10^-14) both strings are empty
if (consecutive_P >= 30) {
break;
}
}
// --- PHASE 2: DECODING OPPONENT'S STRING ---
BigInt M_rec;
// Process history in exact reverse order (LIFO)
for (int i = (int)opp_history.size() - 1; i >= 0; --i) {
int D_opp = opp_history[i].first;
char opp_play = opp_history[i].second;
if (D_opp == 0) {
int s;
if (opp_play == 'P') s = 0;
else if (opp_play == 'K') s = 1;
else s = 2;
M_rec.mul_add(3, s);
}
else if (D_opp == 1) {
int s = (opp_play == 'K') ? 0 : 1;
if (s == 0) {
uint32_t rem = M_rec.div_mod(3);
M_rec.mul_add(4, rem); // Reconstructs: M = M / 3 * 4 + rem
} else {
M_rec.mul_add(4, 3); // Reconstructs: M = M * 4 + 3
}
}
// If D_opp == -1, they were forced to play 'P' and extracted nothing, do nothing.
}
cout << "! " << M_rec.to_bin_string(n) << "\n";
cout.flush();
}
}
int main() {
// Fast I/O
ios_base::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}
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 | #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; // Minimal, fast BigInt optimized for base-N extraction and insertion struct BigInt { vector<uint32_t> limbs; BigInt() {} // Initialize from binary string (most significant bit first) BigInt(const string& bin_str) { for (char c : bin_str) { mul_add(2, c - '0'); } } // M = M * m + a void mul_add(uint32_t m, uint32_t a) { uint64_t carry = a; for (size_t i = 0; i < limbs.size(); ++i) { uint64_t res = (uint64_t)limbs[i] * m + carry; limbs[i] = (uint32_t)res; carry = res >> 32; } if (carry > 0) { limbs.push_back((uint32_t)carry); } } // rem = M % d; M = M / d; return rem; uint32_t div_mod(uint32_t d) { uint64_t rem = 0; for (int i = (int)limbs.size() - 1; i >= 0; --i) { uint64_t cur = (rem << 32) + limbs[i]; limbs[i] = (uint32_t)(cur / d); rem = cur % d; } // Trim leading zeros while (!limbs.empty() && limbs.back() == 0) { limbs.pop_back(); } return (uint32_t)rem; } // Extract binary string of specific length string to_bin_string(int length) { string res; BigInt temp = *this; for (int i = 0; i < length; ++i) { res.push_back((char)('0' + temp.div_mod(2))); } // Since div_mod extracts LSB first, we must reverse the result reverse(res.begin(), res.end()); return res; } }; void solve() { string identity; if (!(cin >> identity)) return; int n, t; cin >> n >> t; while (t--) { string my_str; cin >> my_str; BigInt M_me(my_str); int D = 0; // Score difference: My score - Opponent's score int consecutive_P = 0; // Tie counter for termination // Record states and opponent plays to reverse-decode at the end // pair: {D_opp_before_round, opp_play} vector<pair<int, char>> opp_history; // --- PHASE 1: COMMUNICATION LOOP --- while (true) { char my_play = 'P'; // Extract symbol dynamically based on game state if (D == 0) { int slot = M_me.div_mod(3); if (slot == 0) my_play = 'P'; else if (slot == 1) my_play = 'K'; else my_play = 'N'; } else if (D == 1) { // I'm winning, I must coordinate to prevent hitting D=2 int slot = M_me.div_mod(4); if (slot < 3) { my_play = 'K'; // Lose the round, drop to D=0 M_me.mul_add(3, slot); } else { my_play = 'P'; // Tie the round, stay in D=1 } } else if (D == -1) { // I'm losing, I play deterministic 'P' my_play = 'P'; } cout << my_play << "\n"; cout.flush(); char opp_play; cin >> opp_play; opp_history.push_back({-D, opp_play}); // Count consecutive (P,P) in D=0 state to detect termination safely if (D == 0 && my_play == 'P' && opp_play == 'P') { consecutive_P++; } else { consecutive_P = 0; } // Update Game State if (my_play != opp_play) { if ((my_play == 'P' && opp_play == 'K') || (my_play == 'K' && opp_play == 'N') || (my_play == 'N' && opp_play == 'P')) { D++; } else { D--; } } // 30 consecutive P's guarantees (with probability > 1 - 10^-14) both strings are empty if (consecutive_P >= 30) { break; } } // --- PHASE 2: DECODING OPPONENT'S STRING --- BigInt M_rec; // Process history in exact reverse order (LIFO) for (int i = (int)opp_history.size() - 1; i >= 0; --i) { int D_opp = opp_history[i].first; char opp_play = opp_history[i].second; if (D_opp == 0) { int s; if (opp_play == 'P') s = 0; else if (opp_play == 'K') s = 1; else s = 2; M_rec.mul_add(3, s); } else if (D_opp == 1) { int s = (opp_play == 'K') ? 0 : 1; if (s == 0) { uint32_t rem = M_rec.div_mod(3); M_rec.mul_add(4, rem); // Reconstructs: M = M / 3 * 4 + rem } else { M_rec.mul_add(4, 3); // Reconstructs: M = M * 4 + 3 } } // If D_opp == -1, they were forced to play 'P' and extracted nothing, do nothing. } cout << "! " << M_rec.to_bin_string(n) << "\n"; cout.flush(); } } int main() { // Fast I/O ios_base::sync_with_stdio(false); cin.tie(NULL); solve(); return 0; } |
English