#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
uint64_t seed = 123456789;
uint64_t nextRand() {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
return seed;
}
struct BigInt {
vector<uint32_t> d;
BigInt() {}
BigInt(const string& s) {
if (s.empty()) return;
// Parsujemy bity w grupach po 32
for(int i = 0; i < (int)s.length(); i += 32) {
uint32_t val = 0;
int limit = min((int)s.length(), i + 32);
// Ważne: budujemy wartość bit po bicie od najbardziej znaczącego w danej grupie
for(int j = limit - 1; j >= i; --j) {
val = (val << 1) | (s[j] - '0');
}
d.push_back(val);
}
// Usuwamy wiodące zera (puste grupy 32-bitowe na końcu wektora)
while(!d.empty() && d.back() == 0) d.pop_back();
}
uint32_t divmod(uint32_t m) {
if (d.empty()) return 0;
uint64_t rem = 0;
for(int i = (int)d.size() - 1; i >= 0; --i) {
uint64_t cur = (rem << 32) | d[i];
d[i] = (uint32_t)(cur / m);
rem = cur % m;
}
while(!d.empty() && d.back() == 0) d.pop_back();
return (uint32_t)rem;
}
void muladd(uint32_t m, uint32_t a) {
// Jeśli BigInt jest pusty, traktujemy go jako 0
if (d.empty() && a > 0) d.push_back(0);
uint64_t carry = a;
for(int i = 0; i < (int)d.size(); ++i) {
uint64_t cur = (uint64_t)d[i] * m + carry;
d[i] = (uint32_t)(cur & 0xFFFFFFFF);
carry = cur >> 32;
}
while(carry > 0) {
d.push_back((uint32_t)(carry & 0xFFFFFFFF));
carry >>= 32;
}
}
string getBits(int n) {
string res = "";
for(int i = 0; i < n; ++i) {
res += (char)('0' + divmod(2));
}
return res;
}
};
int main() {
// Szybsze I/O jest kluczowe przy 5000 rund
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string who;
if (!(cin >> who)) return 0;
bool isA = (who == "Algosia");
int n, t;
if (!(cin >> n >> t)) return 0;
char P = 'P', K = 'K', N = 'N';
for(int tc = 0; tc < t; ++tc) {
string s;
if (!(cin >> s)) break;
seed = 123456789;
string xored = s;
for(int i = 0; i < n; ++i) {
xored[i] = ((s[i] - '0') ^ (nextRand() & 1)) + '0';
}
BigInt M(xored);
int S = 0;
vector<char> opMoves;
vector<int> states;
int totalRounds = 5000;
opMoves.reserve(totalRounds);
states.reserve(totalRounds);
for(int r = 0; r < totalRounds; ++r) {
states.push_back(S);
char myM;
// Logika wyboru ruchu na podstawie stanu S i wartości M
if(S == 0) {
int rem = M.divmod(3);
if(rem == 0) myM = P;
else if(rem == 1) myM = K;
else myM = N;
} else if(S == 1) {
if(isA) {
myM = P;
} else {
int rem = M.divmod(2);
if(rem == 0) myM = N;
else myM = P;
}
} else if(S == -1) {
if(isA) {
int rem = M.divmod(2);
if(rem == 0) myM = N;
else myM = P;
} else {
myM = P;
}
} else {
// Fail-safe dla stanów poza zakresem, jeśli gra trwa dalej
myM = P;
}
cout << myM << "\n";
cout.flush();
char opM;
if(!(cin >> opM)) break;
opMoves.push_back(opM);
char aM = isA ? myM : opM;
char bM = isA ? opM : myM;
if(aM != bM) {
if((aM == P && bM == K) || (aM == K && bM == N) || (aM == N && bM == P)) {
S++;
} else {
S--;
}
}
}
// Odtwarzanie liczby przeciwnika
BigInt opM_obj;
for(int r = (int)opMoves.size() - 1; r >= 0; --r) {
int st = states[r];
char opMChar = opMoves[r];
if(st == 0) {
int rem = (opMChar == P) ? 0 : (opMChar == K ? 1 : 2);
opM_obj.muladd(3, rem);
} else if(st == 1) {
if(isA) {
// Jeśli Algosia grała P, to tutaj odtwarzamy ruch Bartka
int rem = (opMChar == N) ? 0 : 1;
opM_obj.muladd(2, rem);
}
} else if(st == -1) {
if(!isA) {
// Jeśli Bartek grał P, odtwarzamy ruch Algosi
int rem = (opMChar == N) ? 0 : 1;
opM_obj.muladd(2, rem);
}
}
}
string opRes = opM_obj.getBits(n);
seed = 123456789;
for(int i = 0; i < n; ++i) {
opRes[i] = ((opRes[i] - '0') ^ (nextRand() & 1)) + '0';
}
cout << "! " << opRes << endl;
}
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 179 180 181 182 183 184 185 186 187 188 189 190 191 | #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; uint64_t seed = 123456789; uint64_t nextRand() { seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17; return seed; } struct BigInt { vector<uint32_t> d; BigInt() {} BigInt(const string& s) { if (s.empty()) return; // Parsujemy bity w grupach po 32 for(int i = 0; i < (int)s.length(); i += 32) { uint32_t val = 0; int limit = min((int)s.length(), i + 32); // Ważne: budujemy wartość bit po bicie od najbardziej znaczącego w danej grupie for(int j = limit - 1; j >= i; --j) { val = (val << 1) | (s[j] - '0'); } d.push_back(val); } // Usuwamy wiodące zera (puste grupy 32-bitowe na końcu wektora) while(!d.empty() && d.back() == 0) d.pop_back(); } uint32_t divmod(uint32_t m) { if (d.empty()) return 0; uint64_t rem = 0; for(int i = (int)d.size() - 1; i >= 0; --i) { uint64_t cur = (rem << 32) | d[i]; d[i] = (uint32_t)(cur / m); rem = cur % m; } while(!d.empty() && d.back() == 0) d.pop_back(); return (uint32_t)rem; } void muladd(uint32_t m, uint32_t a) { // Jeśli BigInt jest pusty, traktujemy go jako 0 if (d.empty() && a > 0) d.push_back(0); uint64_t carry = a; for(int i = 0; i < (int)d.size(); ++i) { uint64_t cur = (uint64_t)d[i] * m + carry; d[i] = (uint32_t)(cur & 0xFFFFFFFF); carry = cur >> 32; } while(carry > 0) { d.push_back((uint32_t)(carry & 0xFFFFFFFF)); carry >>= 32; } } string getBits(int n) { string res = ""; for(int i = 0; i < n; ++i) { res += (char)('0' + divmod(2)); } return res; } }; int main() { // Szybsze I/O jest kluczowe przy 5000 rund ios_base::sync_with_stdio(false); cin.tie(NULL); string who; if (!(cin >> who)) return 0; bool isA = (who == "Algosia"); int n, t; if (!(cin >> n >> t)) return 0; char P = 'P', K = 'K', N = 'N'; for(int tc = 0; tc < t; ++tc) { string s; if (!(cin >> s)) break; seed = 123456789; string xored = s; for(int i = 0; i < n; ++i) { xored[i] = ((s[i] - '0') ^ (nextRand() & 1)) + '0'; } BigInt M(xored); int S = 0; vector<char> opMoves; vector<int> states; int totalRounds = 5000; opMoves.reserve(totalRounds); states.reserve(totalRounds); for(int r = 0; r < totalRounds; ++r) { states.push_back(S); char myM; // Logika wyboru ruchu na podstawie stanu S i wartości M if(S == 0) { int rem = M.divmod(3); if(rem == 0) myM = P; else if(rem == 1) myM = K; else myM = N; } else if(S == 1) { if(isA) { myM = P; } else { int rem = M.divmod(2); if(rem == 0) myM = N; else myM = P; } } else if(S == -1) { if(isA) { int rem = M.divmod(2); if(rem == 0) myM = N; else myM = P; } else { myM = P; } } else { // Fail-safe dla stanów poza zakresem, jeśli gra trwa dalej myM = P; } cout << myM << "\n"; cout.flush(); char opM; if(!(cin >> opM)) break; opMoves.push_back(opM); char aM = isA ? myM : opM; char bM = isA ? opM : myM; if(aM != bM) { if((aM == P && bM == K) || (aM == K && bM == N) || (aM == N && bM == P)) { S++; } else { S--; } } } // Odtwarzanie liczby przeciwnika BigInt opM_obj; for(int r = (int)opMoves.size() - 1; r >= 0; --r) { int st = states[r]; char opMChar = opMoves[r]; if(st == 0) { int rem = (opMChar == P) ? 0 : (opMChar == K ? 1 : 2); opM_obj.muladd(3, rem); } else if(st == 1) { if(isA) { // Jeśli Algosia grała P, to tutaj odtwarzamy ruch Bartka int rem = (opMChar == N) ? 0 : 1; opM_obj.muladd(2, rem); } } else if(st == -1) { if(!isA) { // Jeśli Bartek grał P, odtwarzamy ruch Algosi int rem = (opMChar == N) ? 0 : 1; opM_obj.muladd(2, rem); } } } string opRes = opM_obj.getBits(n); seed = 123456789; for(int i = 0; i < n; ++i) { opRes[i] = ((opRes[i] - '0') ^ (nextRand() & 1)) + '0'; } cout << "! " << opRes << endl; } return 0; } |
English