CF1483E Vabank 题解

Codeforces
Luogu
Solution 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\) 的性质。
但是这个思路肯定行不通,因为肯定可以被卡。

  1. \(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]\)
  2. \(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;}
上一篇:洛谷 P7718 -「EZEC-10」Equalization(差分转化+状压 dp)


下一篇:AT3913 XOR Tree 题解