Link.
Description.
猜数,需要猜出 \(M(M\in[1,10^14])\),有一个权值 \(P\) 初始是 \(1\)。
你每次可以询问一个数 \(X\),分以下三种情况讨论。
- \(X\le M\),返回
Lucky!
,\(P\leftarrow P+X\)。 - \(P\ge X>M\),返回
Fraudster!
,\(P\leftarrow P-X\)。 - \(X>M,X>P\),直接
Wrong Answer
。
交互次数 \(105\) 次。
Solution.
首先你可以用 \(\log\) 次查询得到 \(X\in[2^k,2^{k+1})\),此后 \(P=2^k-1\)
查询过程是直接另 \(X=2^k\) 当 \(P=2^{k+1}-1\) 时
这两步肯定无法优化,而且已经想到了。
关键问题在与得知了 \([L,R]\) 怎么用 \(60\) 步左右求出 \(X\)。
首先有一个 \(2\log V\) 的做法,就是每次查询前查一次 \(L\),防止 \(P<X\)。
但是总复杂度是 \(3\log V \approx 138\),不行。
当然可以用把 \(2\log V\) 变成 \(2\log_{e}V\approx 110\),超了一点。
说明这个东西卡的很紧,应该需要最优决策。
考虑你如果查询了一个 \(X\in[L,R]\),你可以获得什么:
有一种思路就是尝试“铤而走险”,尝试利用 \(X\le M\) 不需要 \(X\le P\) 的性质。
但是这个思路肯定行不通,因为肯定可以被卡。
- \(X\le M\),这样 \(P\leftarrow P+X\),有 \(P\ge R-L+(k+1)L\),就可以调整 \(P\ge R-(X+1)+(k+1)(X+1)\),后来区间是 \([X+1,R]\)
- \(P\ge X\ge M\),这样 \(P\leftarrow P-X\),有 \(P\ge R-L+(k-1)L\),后来区间是 \([L,X-1]\)
所以 \(P\) 可以被表示成 \(R-L+wL\),1 就 \(k\leftarrow k+1\),2 就 \(k\leftarrow k-1\)。
这样就考虑到经典的面试问题——测鸡蛋硬度问题。
设计一个 dp 表示第当前还能抛 \(K\) 次,还有 \(w\) 个“鸡蛋”的方案数。
考虑 1 就相当于扔一次多了一个鸡蛋(怪),2 就碎了一个鸡蛋。
然后就有 \(dp_{K,w}=dp_{K-1,w+1}+dp_{K-1,w-1}+1\)。
每次抛这个分界点,就可以知道最优决策了。
至于调整复杂度,和 Sjy 一样不会证明。
注意需要特判问的数超过 \(10^{14}\) 的情况!!!
Coding.
点击查看代码
//Coded by leapfrog on 2021.11.04 {{{
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
ll M,P=1,dp[55][105],debug;
inline char Ask(ll X)
{
#ifdef ONLINE_JUDGE
if(X>100000000000000) return 0;
printf("? %lld\n",X),fflush(stdout);char ch[15];
scanf("%s",ch);if(ch[1]=='i') exit(0);
if(*ch=='L') return P+=X,1;else return P-=X,0;
#else
debug++;
if(X<=M) return P+=X,1;else if(X>M&&P>=X) return P-=X,0;
puts("OH NO your quertion is wrong!"),exit(0);
#endif
}
inline void Ans(ll x)
{
#ifdef ONLINE_JUDGE
printf("! %lld\n",x),fflush(stdout);
#else
if(x==M) puts("Accepted!");else puts("Wrong Answer /ll");
printf("totally used %lld asks\n",debug);
#endif
}
inline void solve()
{
#ifndef ONLINE_JUDGE
read(M),debug=0;
#endif
ll l=1,r=1e14,rs=0;P=1;if(!Ask(1)) return Ans(0);
for(ll tp=0,v=1;(0^v^0);) if(Ask(tp=P)) l=tp;else {r=tp-1;break;}
Ask(l);int k=1,w=(P>=r);while(dp[k][w]<=r-l) k++;
while(l<=r)
{
while(r-l>dp[k][w]) Ask(l-1),w++;
ll md=l+(w?dp[k-1][w-1]:0);while(P<md) Ask(l-1);
if(Ask(md)) k--,w++,l=md+1,rs=md;else r=md-1,k--,w--;
}Ans(rs);
}
inline void init()
{
for(int i=1;i<=50;i++) for(int j=0;j<=100;j++)
dp[i][j]=(dp[i-1][j+1]+(j?dp[i-1][j-1]+1:0));
}
int main() {int Ca;for(init(),read(Ca);Ca--;) solve();return 0;}