Codeforces 1028G - Guess the number(DP、交互)

涨见识了,交互题居然还可以这么玩……第一次做还真不一定能想得到。

首先此题最棘手的地方在于,如果你问的 \(k>x\) 就自动 gg。

考虑怎样处理这个限制。首先任意时刻我们确定的 \(x\) 的范围肯定是一个区间 \([l,r]\) 对吧。因为存在 \(x=l\) 的可能,所以在这个状态下我们只能问 \(k\) 不超过 \(\min(10^4,l)\) 的问题。因此我们考虑 DP。显然以 \(r\) 为状态,最少询问次数为值域有点太浪费,因此需要定义域与值域交换。这样我们可以得到一个大致的框架:\(dp_{i,j}\) 表示还有 \(i\) 次机会,目前确定的左端点 \(=j\),最多能确定的区间长度是多少。

考虑怎么转移,其实挺 trivial,就无脑根据 \(i-1\) 的 DP 值向后跳 \(j\) 步即可。最后我们发现 \(dp_{5,1}\) 就是题面中出现的那个奇怪的数 \(M\)

求出 DP 值之后交互过程就很容易了。

总之就是用 DP 解决交互题的神仙思路。

const int MAXN=1e4;
const ll M=10004205361450474ll;
ll dp[7][MAXN+5];
void init_dp(){
	for(int i=1;i<=MAXN;i++)dp[1][i]=i;
	for(int i=2;i<=5;i++)for(int j=1;j<=MAXN;j++){
		ll cur=j+dp[i-1][j]-1;
		for(int k=1;k<=j;k++){
			if(cur>=9999){cur+=(j-k+1)*(dp[i-1][MAXN]+1);break;}
			cur++;cur=cur+dp[i-1][cur+1];
		}dp[i][j]=cur-j+1;
	}
}
void work(ll l,ll r,int x){
	// printf("work %lld %lld %d\n",l,r,x);
	int cnt=min(l,(ll)MAXN);
	if(x==1){
		printf("%lld ",r-l+1);
		for(ll i=l;i<=r;i++)printf("%lld ",i);printf("\n");fflush(stdout);
		exit(0);
	}
	vector<ll>vec;
	for(ll cur=l+dp[x-1][cnt]-1,stp=1;stp<=cnt;stp++){++cur;vec.pb(cur);cur+=dp[x-1][min(cur+1,(ll)MAXN)];}
	printf("%d",vec.size());
	for(ll v:vec)printf(" %lld",v);printf("\n");fflush(stdout);
	int t;scanf("%d",&t);
	if(t==-1)exit(0);if(t==0)work(l,vec[0]-1,x-1);
	else if(t==vec.size())work(vec.back()+1,r,x-1);
	else work(vec[t-1]+1,vec[t]-1,x-1);
}
int main(){
	init_dp();assert(dp[5][1]==M);
	work(1,M,5);
	return 0;
}
posted @ 2022-12-22 13:18  tzc_wk  阅读(44)  评论(0)    收藏  举报