首先第一次詢問肯定是問 \(\texttt{aa}\),答案減去 \(1\) 得到基數 \(p\)。
然後我們隨意詢問一個真實 Hash 值(取模之前)\(X\) 大於模數 \(m\) 的字串,例如 \(s=\texttt{zzz}\cdots\texttt{zzz}\)(\(50\) 個 \(\texttt z\))。設它取模得到的 Hash 值是 \(a\)。
考慮正整數 \(1 \leq b \leq a+1\)。注意到 \((X-a-b) \bmod m= ((X-a) \bmod m + (m-b)) \bmod m = m-b\),因為 \(a < m\)。
這一步注意力非常集中!
那麼我們只要能找到 Hash 值在 \([X-1-2a,X-1-a]\) 之間的字串就結束了。
首先將 \(s_1\) 減去 \(1\),並令 \(b\) 為 \(1\)。然後從低到高遍歷 \(s\) 的每一位,並計算 \(a\) 在這一位上的大小 \(v\)。
如果 \(v\) 不超過 \(24\),那麼 \(s\) 的這一位必然可以減去 \(v\),因為 \(s_i\) 最開始是 \(\texttt{z}(26)\),就算之前被減去了 \(1\),還剩下 \(25\)。
如果 \(v\) 超過了 \(24\),那麼 \(s\) 的下一位減去 \(1\),這樣會讓 \(b\) 增加 \((p-v)\times W\),其中 \(W\) 是這一位的位權。
我們注意到 \(p \leq 50\),因為 \(p-v \leq 25\),從而當 \(v = 25\) 的時候 \((p-v) \times W\) 取得最大值 $ = 25W$。因此,就算這個過程中 \((p-v) \times W\) 一直取得最大值,\(b\) 在這個過程中也不會增加一個超過 \(a\) 的量,因此 \(b\) 最多為 \(a+1\),這使得我們的構造成立。
# include <bits/stdc++.h>
const int N=100010,INF=0x3f3f3f3f;
inline void solve(void){
std::cout<<"? aa"<<std::endl;
int p;
std::cin>>p;
--p;
std::string s(50,'z');
std::cout<<"? "<<s<<std::endl;
int a,b=1;
std::cin>>a;
--s[0];
for(long long i=0,base=1;a>=base;++i,base*=p){
int v=(a/base)%p;
if(v<=24) s[i]-=v;
else --s[i+1],b+=base*(p-v);
}
std::cout<<"? "<<s<<std::endl;
int c;
std::cin>>c;
std::cout<<"! "<<p<<" "<<b+c<<std::endl;
return;
}
int main(void){
int T;
std::cin>>T;
while(T--) solve();
return 0;
}